blob: 36c921986bb3c43d84cdd6f9047775af9b5a4472 [file] [log] [blame]
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "code_generator_mips.h"
18
Alexey Frunze4147fcc2017-06-17 19:57:27 -070019#include "arch/mips/asm_support_mips.h"
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020020#include "arch/mips/entrypoints_direct_mips.h"
21#include "arch/mips/instruction_set_features_mips.h"
22#include "art_method.h"
Vladimir Marko94ec2db2017-09-06 17:21:03 +010023#include "class_table.h"
Chris Larsen701566a2015-10-27 15:29:13 -070024#include "code_generator_utils.h"
Vladimir Marko3a21e382016-09-02 12:38:38 +010025#include "compiled_method.h"
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020026#include "entrypoints/quick/quick_entrypoints.h"
27#include "entrypoints/quick/quick_entrypoints_enum.h"
28#include "gc/accounting/card_table.h"
Andreas Gampe09659c22017-09-18 18:23:32 -070029#include "heap_poisoning.h"
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020030#include "intrinsics.h"
Chris Larsen701566a2015-10-27 15:29:13 -070031#include "intrinsics_mips.h"
Vladimir Markod8dbc8d2017-09-20 13:37:47 +010032#include "linker/linker_patch.h"
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020033#include "mirror/array-inl.h"
34#include "mirror/class-inl.h"
35#include "offsets.h"
Vladimir Marko174b2e22017-10-12 13:34:49 +010036#include "stack_map_stream.h"
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020037#include "thread.h"
38#include "utils/assembler.h"
39#include "utils/mips/assembler_mips.h"
40#include "utils/stack_checks.h"
41
42namespace art {
43namespace mips {
44
45static constexpr int kCurrentMethodStackOffset = 0;
46static constexpr Register kMethodRegisterArgument = A0;
47
Alexey Frunze4147fcc2017-06-17 19:57:27 -070048// Flags controlling the use of thunks for Baker read barriers.
49constexpr bool kBakerReadBarrierThunksEnableForFields = true;
50constexpr bool kBakerReadBarrierThunksEnableForArrays = true;
51constexpr bool kBakerReadBarrierThunksEnableForGcRoots = true;
52
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010053Location MipsReturnLocation(DataType::Type return_type) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020054 switch (return_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +010055 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010056 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +010057 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010058 case DataType::Type::kInt8:
59 case DataType::Type::kUint16:
60 case DataType::Type::kInt16:
Aart Bik66c158e2018-01-31 12:55:04 -080061 case DataType::Type::kUint32:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010062 case DataType::Type::kInt32:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020063 return Location::RegisterLocation(V0);
64
Aart Bik66c158e2018-01-31 12:55:04 -080065 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010066 case DataType::Type::kInt64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020067 return Location::RegisterPairLocation(V0, V1);
68
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010069 case DataType::Type::kFloat32:
70 case DataType::Type::kFloat64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020071 return Location::FpuRegisterLocation(F0);
72
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010073 case DataType::Type::kVoid:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020074 return Location();
75 }
76 UNREACHABLE();
77}
78
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010079Location InvokeDexCallingConventionVisitorMIPS::GetReturnLocation(DataType::Type type) const {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020080 return MipsReturnLocation(type);
81}
82
83Location InvokeDexCallingConventionVisitorMIPS::GetMethodLocation() const {
84 return Location::RegisterLocation(kMethodRegisterArgument);
85}
86
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010087Location InvokeDexCallingConventionVisitorMIPS::GetNextLocation(DataType::Type type) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020088 Location next_location;
89
90 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +010091 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010092 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +010093 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010094 case DataType::Type::kInt8:
95 case DataType::Type::kUint16:
96 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +010097 case DataType::Type::kInt32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +020098 uint32_t gp_index = gp_index_++;
99 if (gp_index < calling_convention.GetNumberOfRegisters()) {
100 next_location = Location::RegisterLocation(calling_convention.GetRegisterAt(gp_index));
101 } else {
102 size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
103 next_location = Location::StackSlot(stack_offset);
104 }
105 break;
106 }
107
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100108 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200109 uint32_t gp_index = gp_index_;
110 gp_index_ += 2;
111 if (gp_index + 1 < calling_convention.GetNumberOfRegisters()) {
Alexey Frunze1b8464d2016-11-12 17:22:05 -0800112 Register reg = calling_convention.GetRegisterAt(gp_index);
113 if (reg == A1 || reg == A3) {
114 gp_index_++; // Skip A1(A3), and use A2_A3(T0_T1) instead.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200115 gp_index++;
116 }
117 Register low_even = calling_convention.GetRegisterAt(gp_index);
118 Register high_odd = calling_convention.GetRegisterAt(gp_index + 1);
119 DCHECK_EQ(low_even + 1, high_odd);
120 next_location = Location::RegisterPairLocation(low_even, high_odd);
121 } else {
122 size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
123 next_location = Location::DoubleStackSlot(stack_offset);
124 }
125 break;
126 }
127
128 // Note: both float and double types are stored in even FPU registers. On 32 bit FPU, double
129 // will take up the even/odd pair, while floats are stored in even regs only.
130 // On 64 bit FPU, both double and float are stored in even registers only.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100131 case DataType::Type::kFloat32:
132 case DataType::Type::kFloat64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200133 uint32_t float_index = float_index_++;
134 if (float_index < calling_convention.GetNumberOfFpuRegisters()) {
135 next_location = Location::FpuRegisterLocation(
136 calling_convention.GetFpuRegisterAt(float_index));
137 } else {
138 size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100139 next_location = DataType::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
140 : Location::StackSlot(stack_offset);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200141 }
142 break;
143 }
144
Aart Bik66c158e2018-01-31 12:55:04 -0800145 case DataType::Type::kUint32:
146 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100147 case DataType::Type::kVoid:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200148 LOG(FATAL) << "Unexpected parameter type " << type;
149 break;
150 }
151
152 // Space on the stack is reserved for all arguments.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100153 stack_index_ += DataType::Is64BitType(type) ? 2 : 1;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200154
155 return next_location;
156}
157
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100158Location InvokeRuntimeCallingConvention::GetReturnLocation(DataType::Type type) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200159 return MipsReturnLocation(type);
160}
161
Roland Levillain7cbd27f2016-08-11 23:53:33 +0100162// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
163#define __ down_cast<CodeGeneratorMIPS*>(codegen)->GetAssembler()-> // NOLINT
Andreas Gampe542451c2016-07-26 09:02:02 -0700164#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, x).Int32Value()
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200165
166class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS {
167 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000168 explicit BoundsCheckSlowPathMIPS(HBoundsCheck* instruction) : SlowPathCodeMIPS(instruction) {}
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200169
170 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
171 LocationSummary* locations = instruction_->GetLocations();
172 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
173 __ Bind(GetEntryLabel());
174 if (instruction_->CanThrowIntoCatchBlock()) {
175 // Live registers will be restored in the catch block if caught.
176 SaveLiveRegisters(codegen, instruction_->GetLocations());
177 }
178 // We're moving two locations to locations that could overlap, so we need a parallel
179 // move resolver.
180 InvokeRuntimeCallingConvention calling_convention;
181 codegen->EmitParallelMoves(locations->InAt(0),
182 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100183 DataType::Type::kInt32,
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200184 locations->InAt(1),
185 Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100186 DataType::Type::kInt32);
Serban Constantinescufca16662016-07-14 09:21:59 +0100187 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
188 ? kQuickThrowStringBounds
189 : kQuickThrowArrayBounds;
190 mips_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
Vladimir Marko87f3fcb2016-04-28 15:52:11 +0100191 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200192 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
193 }
194
195 bool IsFatal() const OVERRIDE { return true; }
196
197 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathMIPS"; }
198
199 private:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200200 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS);
201};
202
203class DivZeroCheckSlowPathMIPS : public SlowPathCodeMIPS {
204 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000205 explicit DivZeroCheckSlowPathMIPS(HDivZeroCheck* instruction) : SlowPathCodeMIPS(instruction) {}
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200206
207 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
208 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
209 __ Bind(GetEntryLabel());
Serban Constantinescufca16662016-07-14 09:21:59 +0100210 mips_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200211 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
212 }
213
214 bool IsFatal() const OVERRIDE { return true; }
215
216 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathMIPS"; }
217
218 private:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200219 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS);
220};
221
222class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
223 public:
224 LoadClassSlowPathMIPS(HLoadClass* cls,
225 HInstruction* at,
226 uint32_t dex_pc,
Vladimir Markof3c52b42017-11-17 17:32:12 +0000227 bool do_clinit)
Alexey Frunze5fa5c042017-06-01 21:07:52 -0700228 : SlowPathCodeMIPS(at),
229 cls_(cls),
230 dex_pc_(dex_pc),
Vladimir Markof3c52b42017-11-17 17:32:12 +0000231 do_clinit_(do_clinit) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200232 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
233 }
234
235 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000236 LocationSummary* locations = instruction_->GetLocations();
Alexey Frunzec61c0762017-04-10 13:54:23 -0700237 Location out = locations->Out();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200238 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
Alexey Frunzec61c0762017-04-10 13:54:23 -0700239 InvokeRuntimeCallingConvention calling_convention;
240 DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200241 __ Bind(GetEntryLabel());
242 SaveLiveRegisters(codegen, locations);
243
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000244 dex::TypeIndex type_index = cls_->GetTypeIndex();
245 __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_);
Serban Constantinescufca16662016-07-14 09:21:59 +0100246 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
247 : kQuickInitializeType;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000248 mips_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200249 if (do_clinit_) {
250 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
251 } else {
252 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
253 }
254
255 // Move the class to the desired location.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200256 if (out.IsValid()) {
257 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100258 DataType::Type type = instruction_->GetType();
Alexey Frunzec61c0762017-04-10 13:54:23 -0700259 mips_codegen->MoveLocation(out,
260 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
261 type);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200262 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200263 RestoreLiveRegisters(codegen, locations);
Alexey Frunzec61c0762017-04-10 13:54:23 -0700264
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200265 __ B(GetExitLabel());
266 }
267
268 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathMIPS"; }
269
270 private:
271 // The class this slow path will load.
272 HLoadClass* const cls_;
273
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200274 // The dex PC of `at_`.
275 const uint32_t dex_pc_;
276
277 // Whether to initialize the class.
278 const bool do_clinit_;
279
280 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS);
281};
282
283class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
284 public:
Vladimir Markof3c52b42017-11-17 17:32:12 +0000285 explicit LoadStringSlowPathMIPS(HLoadString* instruction)
286 : SlowPathCodeMIPS(instruction) {}
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200287
288 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Alexey Frunzec61c0762017-04-10 13:54:23 -0700289 DCHECK(instruction_->IsLoadString());
290 DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200291 LocationSummary* locations = instruction_->GetLocations();
292 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
Vladimir Markof3c52b42017-11-17 17:32:12 +0000293 const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200294 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
Alexey Frunzec61c0762017-04-10 13:54:23 -0700295 InvokeRuntimeCallingConvention calling_convention;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200296 __ Bind(GetEntryLabel());
297 SaveLiveRegisters(codegen, locations);
298
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000299 __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_);
Serban Constantinescufca16662016-07-14 09:21:59 +0100300 mips_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200301 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Alexey Frunzec61c0762017-04-10 13:54:23 -0700302
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100303 DataType::Type type = instruction_->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200304 mips_codegen->MoveLocation(locations->Out(),
Alexey Frunzec61c0762017-04-10 13:54:23 -0700305 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200306 type);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200307 RestoreLiveRegisters(codegen, locations);
Vladimir Markoaad75c62016-10-03 08:46:48 +0000308
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200309 __ B(GetExitLabel());
310 }
311
312 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS"; }
313
314 private:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200315 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS);
316};
317
318class NullCheckSlowPathMIPS : public SlowPathCodeMIPS {
319 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000320 explicit NullCheckSlowPathMIPS(HNullCheck* instr) : SlowPathCodeMIPS(instr) {}
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200321
322 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
323 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
324 __ Bind(GetEntryLabel());
325 if (instruction_->CanThrowIntoCatchBlock()) {
326 // Live registers will be restored in the catch block if caught.
327 SaveLiveRegisters(codegen, instruction_->GetLocations());
328 }
Serban Constantinescufca16662016-07-14 09:21:59 +0100329 mips_codegen->InvokeRuntime(kQuickThrowNullPointer,
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200330 instruction_,
331 instruction_->GetDexPc(),
Serban Constantinescufca16662016-07-14 09:21:59 +0100332 this);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200333 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
334 }
335
336 bool IsFatal() const OVERRIDE { return true; }
337
338 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathMIPS"; }
339
340 private:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200341 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS);
342};
343
344class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS {
345 public:
346 SuspendCheckSlowPathMIPS(HSuspendCheck* instruction, HBasicBlock* successor)
David Srbecky9cd6d372016-02-09 15:24:47 +0000347 : SlowPathCodeMIPS(instruction), successor_(successor) {}
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200348
349 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Lena Djokicca8c2952017-05-29 11:31:46 +0200350 LocationSummary* locations = instruction_->GetLocations();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200351 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
352 __ Bind(GetEntryLabel());
Lena Djokicca8c2952017-05-29 11:31:46 +0200353 SaveLiveRegisters(codegen, locations); // Only saves live vector registers for SIMD.
Serban Constantinescufca16662016-07-14 09:21:59 +0100354 mips_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200355 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
Lena Djokicca8c2952017-05-29 11:31:46 +0200356 RestoreLiveRegisters(codegen, locations); // Only restores live vector registers for SIMD.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200357 if (successor_ == nullptr) {
358 __ B(GetReturnLabel());
359 } else {
360 __ B(mips_codegen->GetLabelOf(successor_));
361 }
362 }
363
364 MipsLabel* GetReturnLabel() {
365 DCHECK(successor_ == nullptr);
366 return &return_label_;
367 }
368
369 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS"; }
370
Chris Larsena2045912017-11-02 12:39:54 -0700371 HBasicBlock* GetSuccessor() const {
372 return successor_;
373 }
374
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200375 private:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200376 // If not null, the block to branch to after the suspend check.
377 HBasicBlock* const successor_;
378
379 // If `successor_` is null, the label to branch to after the suspend check.
380 MipsLabel return_label_;
381
382 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathMIPS);
383};
384
385class TypeCheckSlowPathMIPS : public SlowPathCodeMIPS {
386 public:
Alexey Frunze66b69ad2017-02-24 00:51:44 -0800387 explicit TypeCheckSlowPathMIPS(HInstruction* instruction, bool is_fatal)
388 : SlowPathCodeMIPS(instruction), is_fatal_(is_fatal) {}
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200389
390 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
391 LocationSummary* locations = instruction_->GetLocations();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200392 uint32_t dex_pc = instruction_->GetDexPc();
393 DCHECK(instruction_->IsCheckCast()
394 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
395 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
396
397 __ Bind(GetEntryLabel());
Alexey Frunze66b69ad2017-02-24 00:51:44 -0800398 if (!is_fatal_) {
399 SaveLiveRegisters(codegen, locations);
400 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200401
402 // We're moving two locations to locations that could overlap, so we need a parallel
403 // move resolver.
404 InvokeRuntimeCallingConvention calling_convention;
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800405 codegen->EmitParallelMoves(locations->InAt(0),
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200406 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100407 DataType::Type::kReference,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800408 locations->InAt(1),
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200409 Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100410 DataType::Type::kReference);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200411 if (instruction_->IsInstanceOf()) {
Serban Constantinescufca16662016-07-14 09:21:59 +0100412 mips_codegen->InvokeRuntime(kQuickInstanceofNonTrivial, instruction_, dex_pc, this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800413 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100414 DataType::Type ret_type = instruction_->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200415 Location ret_loc = calling_convention.GetReturnLocation(ret_type);
416 mips_codegen->MoveLocation(locations->Out(), ret_loc, ret_type);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200417 } else {
418 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800419 mips_codegen->InvokeRuntime(kQuickCheckInstanceOf, instruction_, dex_pc, this);
420 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200421 }
422
Alexey Frunze66b69ad2017-02-24 00:51:44 -0800423 if (!is_fatal_) {
424 RestoreLiveRegisters(codegen, locations);
425 __ B(GetExitLabel());
426 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200427 }
428
429 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathMIPS"; }
430
Alexey Frunze66b69ad2017-02-24 00:51:44 -0800431 bool IsFatal() const OVERRIDE { return is_fatal_; }
432
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200433 private:
Alexey Frunze66b69ad2017-02-24 00:51:44 -0800434 const bool is_fatal_;
435
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200436 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS);
437};
438
439class DeoptimizationSlowPathMIPS : public SlowPathCodeMIPS {
440 public:
Aart Bik42249c32016-01-07 15:33:50 -0800441 explicit DeoptimizationSlowPathMIPS(HDeoptimize* instruction)
David Srbecky9cd6d372016-02-09 15:24:47 +0000442 : SlowPathCodeMIPS(instruction) {}
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200443
444 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Aart Bik42249c32016-01-07 15:33:50 -0800445 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200446 __ Bind(GetEntryLabel());
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100447 LocationSummary* locations = instruction_->GetLocations();
448 SaveLiveRegisters(codegen, locations);
449 InvokeRuntimeCallingConvention calling_convention;
450 __ LoadConst32(calling_convention.GetRegisterAt(0),
451 static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
Serban Constantinescufca16662016-07-14 09:21:59 +0100452 mips_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100453 CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200454 }
455
456 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathMIPS"; }
457
458 private:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200459 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS);
460};
461
Alexey Frunze15958152017-02-09 19:08:30 -0800462class ArraySetSlowPathMIPS : public SlowPathCodeMIPS {
463 public:
464 explicit ArraySetSlowPathMIPS(HInstruction* instruction) : SlowPathCodeMIPS(instruction) {}
465
466 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
467 LocationSummary* locations = instruction_->GetLocations();
468 __ Bind(GetEntryLabel());
469 SaveLiveRegisters(codegen, locations);
470
471 InvokeRuntimeCallingConvention calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100472 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Alexey Frunze15958152017-02-09 19:08:30 -0800473 parallel_move.AddMove(
474 locations->InAt(0),
475 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100476 DataType::Type::kReference,
Alexey Frunze15958152017-02-09 19:08:30 -0800477 nullptr);
478 parallel_move.AddMove(
479 locations->InAt(1),
480 Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100481 DataType::Type::kInt32,
Alexey Frunze15958152017-02-09 19:08:30 -0800482 nullptr);
483 parallel_move.AddMove(
484 locations->InAt(2),
485 Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100486 DataType::Type::kReference,
Alexey Frunze15958152017-02-09 19:08:30 -0800487 nullptr);
488 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
489
490 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
491 mips_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
492 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
493 RestoreLiveRegisters(codegen, locations);
494 __ B(GetExitLabel());
495 }
496
497 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathMIPS"; }
498
499 private:
500 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathMIPS);
501};
502
503// Slow path marking an object reference `ref` during a read
504// barrier. The field `obj.field` in the object `obj` holding this
505// reference does not get updated by this slow path after marking (see
506// ReadBarrierMarkAndUpdateFieldSlowPathMIPS below for that).
507//
508// This means that after the execution of this slow path, `ref` will
509// always be up-to-date, but `obj.field` may not; i.e., after the
510// flip, `ref` will be a to-space reference, but `obj.field` will
511// probably still be a from-space reference (unless it gets updated by
512// another thread, or if another thread installed another object
513// reference (different from `ref`) in `obj.field`).
514//
515// If `entrypoint` is a valid location it is assumed to already be
516// holding the entrypoint. The case where the entrypoint is passed in
517// is for the GcRoot read barrier.
518class ReadBarrierMarkSlowPathMIPS : public SlowPathCodeMIPS {
519 public:
520 ReadBarrierMarkSlowPathMIPS(HInstruction* instruction,
521 Location ref,
522 Location entrypoint = Location::NoLocation())
523 : SlowPathCodeMIPS(instruction), ref_(ref), entrypoint_(entrypoint) {
524 DCHECK(kEmitCompilerReadBarrier);
525 }
526
527 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathMIPS"; }
528
529 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
530 LocationSummary* locations = instruction_->GetLocations();
531 Register ref_reg = ref_.AsRegister<Register>();
532 DCHECK(locations->CanCall());
533 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
534 DCHECK(instruction_->IsInstanceFieldGet() ||
535 instruction_->IsStaticFieldGet() ||
536 instruction_->IsArrayGet() ||
537 instruction_->IsArraySet() ||
538 instruction_->IsLoadClass() ||
539 instruction_->IsLoadString() ||
540 instruction_->IsInstanceOf() ||
541 instruction_->IsCheckCast() ||
542 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
543 (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
544 << "Unexpected instruction in read barrier marking slow path: "
545 << instruction_->DebugName();
546
547 __ Bind(GetEntryLabel());
548 // No need to save live registers; it's taken care of by the
549 // entrypoint. Also, there is no need to update the stack mask,
550 // as this runtime call will not trigger a garbage collection.
551 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
552 DCHECK((V0 <= ref_reg && ref_reg <= T7) ||
553 (S2 <= ref_reg && ref_reg <= S7) ||
554 (ref_reg == FP)) << ref_reg;
555 // "Compact" slow path, saving two moves.
556 //
557 // Instead of using the standard runtime calling convention (input
558 // and output in A0 and V0 respectively):
559 //
560 // A0 <- ref
561 // V0 <- ReadBarrierMark(A0)
562 // ref <- V0
563 //
564 // we just use rX (the register containing `ref`) as input and output
565 // of a dedicated entrypoint:
566 //
567 // rX <- ReadBarrierMarkRegX(rX)
568 //
569 if (entrypoint_.IsValid()) {
570 mips_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
571 DCHECK_EQ(entrypoint_.AsRegister<Register>(), T9);
572 __ Jalr(entrypoint_.AsRegister<Register>());
573 __ NopIfNoReordering();
574 } else {
575 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100576 Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(ref_reg - 1);
Alexey Frunze15958152017-02-09 19:08:30 -0800577 // This runtime call does not require a stack map.
578 mips_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
579 instruction_,
580 this,
581 /* direct */ false);
582 }
583 __ B(GetExitLabel());
584 }
585
586 private:
587 // The location (register) of the marked object reference.
588 const Location ref_;
589
590 // The location of the entrypoint if already loaded.
591 const Location entrypoint_;
592
593 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathMIPS);
594};
595
596// Slow path marking an object reference `ref` during a read barrier,
597// and if needed, atomically updating the field `obj.field` in the
598// object `obj` holding this reference after marking (contrary to
599// ReadBarrierMarkSlowPathMIPS above, which never tries to update
600// `obj.field`).
601//
602// This means that after the execution of this slow path, both `ref`
603// and `obj.field` will be up-to-date; i.e., after the flip, both will
604// hold the same to-space reference (unless another thread installed
605// another object reference (different from `ref`) in `obj.field`).
606class ReadBarrierMarkAndUpdateFieldSlowPathMIPS : public SlowPathCodeMIPS {
607 public:
608 ReadBarrierMarkAndUpdateFieldSlowPathMIPS(HInstruction* instruction,
609 Location ref,
610 Register obj,
611 Location field_offset,
612 Register temp1)
613 : SlowPathCodeMIPS(instruction),
614 ref_(ref),
615 obj_(obj),
616 field_offset_(field_offset),
617 temp1_(temp1) {
618 DCHECK(kEmitCompilerReadBarrier);
619 }
620
621 const char* GetDescription() const OVERRIDE {
622 return "ReadBarrierMarkAndUpdateFieldSlowPathMIPS";
623 }
624
625 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
626 LocationSummary* locations = instruction_->GetLocations();
627 Register ref_reg = ref_.AsRegister<Register>();
628 DCHECK(locations->CanCall());
629 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
630 // This slow path is only used by the UnsafeCASObject intrinsic.
631 DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
632 << "Unexpected instruction in read barrier marking and field updating slow path: "
633 << instruction_->DebugName();
634 DCHECK(instruction_->GetLocations()->Intrinsified());
635 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
636 DCHECK(field_offset_.IsRegisterPair()) << field_offset_;
637
638 __ Bind(GetEntryLabel());
639
640 // Save the old reference.
641 // Note that we cannot use AT or TMP to save the old reference, as those
642 // are used by the code that follows, but we need the old reference after
643 // the call to the ReadBarrierMarkRegX entry point.
644 DCHECK_NE(temp1_, AT);
645 DCHECK_NE(temp1_, TMP);
646 __ Move(temp1_, ref_reg);
647
648 // No need to save live registers; it's taken care of by the
649 // entrypoint. Also, there is no need to update the stack mask,
650 // as this runtime call will not trigger a garbage collection.
651 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
652 DCHECK((V0 <= ref_reg && ref_reg <= T7) ||
653 (S2 <= ref_reg && ref_reg <= S7) ||
654 (ref_reg == FP)) << ref_reg;
655 // "Compact" slow path, saving two moves.
656 //
657 // Instead of using the standard runtime calling convention (input
658 // and output in A0 and V0 respectively):
659 //
660 // A0 <- ref
661 // V0 <- ReadBarrierMark(A0)
662 // ref <- V0
663 //
664 // we just use rX (the register containing `ref`) as input and output
665 // of a dedicated entrypoint:
666 //
667 // rX <- ReadBarrierMarkRegX(rX)
668 //
669 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100670 Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(ref_reg - 1);
Alexey Frunze15958152017-02-09 19:08:30 -0800671 // This runtime call does not require a stack map.
672 mips_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
673 instruction_,
674 this,
675 /* direct */ false);
676
677 // If the new reference is different from the old reference,
678 // update the field in the holder (`*(obj_ + field_offset_)`).
679 //
680 // Note that this field could also hold a different object, if
681 // another thread had concurrently changed it. In that case, the
682 // the compare-and-set (CAS) loop below would abort, leaving the
683 // field as-is.
684 MipsLabel done;
685 __ Beq(temp1_, ref_reg, &done);
686
687 // Update the the holder's field atomically. This may fail if
688 // mutator updates before us, but it's OK. This is achieved
689 // using a strong compare-and-set (CAS) operation with relaxed
690 // memory synchronization ordering, where the expected value is
691 // the old reference and the desired value is the new reference.
692
693 // Convenience aliases.
694 Register base = obj_;
695 // The UnsafeCASObject intrinsic uses a register pair as field
696 // offset ("long offset"), of which only the low part contains
697 // data.
698 Register offset = field_offset_.AsRegisterPairLow<Register>();
699 Register expected = temp1_;
700 Register value = ref_reg;
701 Register tmp_ptr = TMP; // Pointer to actual memory.
702 Register tmp = AT; // Value in memory.
703
704 __ Addu(tmp_ptr, base, offset);
705
706 if (kPoisonHeapReferences) {
707 __ PoisonHeapReference(expected);
708 // Do not poison `value` if it is the same register as
709 // `expected`, which has just been poisoned.
710 if (value != expected) {
711 __ PoisonHeapReference(value);
712 }
713 }
714
715 // do {
716 // tmp = [r_ptr] - expected;
717 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
718
719 bool is_r6 = mips_codegen->GetInstructionSetFeatures().IsR6();
720 MipsLabel loop_head, exit_loop;
721 __ Bind(&loop_head);
722 if (is_r6) {
723 __ LlR6(tmp, tmp_ptr);
724 } else {
725 __ LlR2(tmp, tmp_ptr);
726 }
727 __ Bne(tmp, expected, &exit_loop);
728 __ Move(tmp, value);
729 if (is_r6) {
730 __ ScR6(tmp, tmp_ptr);
731 } else {
732 __ ScR2(tmp, tmp_ptr);
733 }
734 __ Beqz(tmp, &loop_head);
735 __ Bind(&exit_loop);
736
737 if (kPoisonHeapReferences) {
738 __ UnpoisonHeapReference(expected);
739 // Do not unpoison `value` if it is the same register as
740 // `expected`, which has just been unpoisoned.
741 if (value != expected) {
742 __ UnpoisonHeapReference(value);
743 }
744 }
745
746 __ Bind(&done);
747 __ B(GetExitLabel());
748 }
749
750 private:
751 // The location (register) of the marked object reference.
752 const Location ref_;
753 // The register containing the object holding the marked object reference field.
754 const Register obj_;
755 // The location of the offset of the marked reference field within `obj_`.
756 Location field_offset_;
757
758 const Register temp1_;
759
760 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathMIPS);
761};
762
763// Slow path generating a read barrier for a heap reference.
764class ReadBarrierForHeapReferenceSlowPathMIPS : public SlowPathCodeMIPS {
765 public:
766 ReadBarrierForHeapReferenceSlowPathMIPS(HInstruction* instruction,
767 Location out,
768 Location ref,
769 Location obj,
770 uint32_t offset,
771 Location index)
772 : SlowPathCodeMIPS(instruction),
773 out_(out),
774 ref_(ref),
775 obj_(obj),
776 offset_(offset),
777 index_(index) {
778 DCHECK(kEmitCompilerReadBarrier);
779 // If `obj` is equal to `out` or `ref`, it means the initial object
780 // has been overwritten by (or after) the heap object reference load
781 // to be instrumented, e.g.:
782 //
783 // __ LoadFromOffset(kLoadWord, out, out, offset);
784 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
785 //
786 // In that case, we have lost the information about the original
787 // object, and the emitted read barrier cannot work properly.
788 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
789 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
790 }
791
792 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
793 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
794 LocationSummary* locations = instruction_->GetLocations();
795 Register reg_out = out_.AsRegister<Register>();
796 DCHECK(locations->CanCall());
797 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
798 DCHECK(instruction_->IsInstanceFieldGet() ||
799 instruction_->IsStaticFieldGet() ||
800 instruction_->IsArrayGet() ||
801 instruction_->IsInstanceOf() ||
802 instruction_->IsCheckCast() ||
803 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
804 << "Unexpected instruction in read barrier for heap reference slow path: "
805 << instruction_->DebugName();
806
807 __ Bind(GetEntryLabel());
808 SaveLiveRegisters(codegen, locations);
809
810 // We may have to change the index's value, but as `index_` is a
811 // constant member (like other "inputs" of this slow path),
812 // introduce a copy of it, `index`.
813 Location index = index_;
814 if (index_.IsValid()) {
815 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
816 if (instruction_->IsArrayGet()) {
817 // Compute the actual memory offset and store it in `index`.
818 Register index_reg = index_.AsRegister<Register>();
819 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg));
820 if (codegen->IsCoreCalleeSaveRegister(index_reg)) {
821 // We are about to change the value of `index_reg` (see the
822 // calls to art::mips::MipsAssembler::Sll and
823 // art::mips::MipsAssembler::Addiu32 below), but it has
824 // not been saved by the previous call to
825 // art::SlowPathCode::SaveLiveRegisters, as it is a
826 // callee-save register --
827 // art::SlowPathCode::SaveLiveRegisters does not consider
828 // callee-save registers, as it has been designed with the
829 // assumption that callee-save registers are supposed to be
830 // handled by the called function. So, as a callee-save
831 // register, `index_reg` _would_ eventually be saved onto
832 // the stack, but it would be too late: we would have
833 // changed its value earlier. Therefore, we manually save
834 // it here into another freely available register,
835 // `free_reg`, chosen of course among the caller-save
836 // registers (as a callee-save `free_reg` register would
837 // exhibit the same problem).
838 //
839 // Note we could have requested a temporary register from
840 // the register allocator instead; but we prefer not to, as
841 // this is a slow path, and we know we can find a
842 // caller-save register that is available.
843 Register free_reg = FindAvailableCallerSaveRegister(codegen);
844 __ Move(free_reg, index_reg);
845 index_reg = free_reg;
846 index = Location::RegisterLocation(index_reg);
847 } else {
848 // The initial register stored in `index_` has already been
849 // saved in the call to art::SlowPathCode::SaveLiveRegisters
850 // (as it is not a callee-save register), so we can freely
851 // use it.
852 }
853 // Shifting the index value contained in `index_reg` by the scale
854 // factor (2) cannot overflow in practice, as the runtime is
855 // unable to allocate object arrays with a size larger than
856 // 2^26 - 1 (that is, 2^28 - 4 bytes).
857 __ Sll(index_reg, index_reg, TIMES_4);
858 static_assert(
859 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
860 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
861 __ Addiu32(index_reg, index_reg, offset_);
862 } else {
863 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
864 // intrinsics, `index_` is not shifted by a scale factor of 2
865 // (as in the case of ArrayGet), as it is actually an offset
866 // to an object field within an object.
867 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
868 DCHECK(instruction_->GetLocations()->Intrinsified());
869 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
870 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
871 << instruction_->AsInvoke()->GetIntrinsic();
872 DCHECK_EQ(offset_, 0U);
873 DCHECK(index_.IsRegisterPair());
874 // UnsafeGet's offset location is a register pair, the low
875 // part contains the correct offset.
876 index = index_.ToLow();
877 }
878 }
879
880 // We're moving two or three locations to locations that could
881 // overlap, so we need a parallel move resolver.
882 InvokeRuntimeCallingConvention calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100883 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Alexey Frunze15958152017-02-09 19:08:30 -0800884 parallel_move.AddMove(ref_,
885 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100886 DataType::Type::kReference,
Alexey Frunze15958152017-02-09 19:08:30 -0800887 nullptr);
888 parallel_move.AddMove(obj_,
889 Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100890 DataType::Type::kReference,
Alexey Frunze15958152017-02-09 19:08:30 -0800891 nullptr);
892 if (index.IsValid()) {
893 parallel_move.AddMove(index,
894 Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100895 DataType::Type::kInt32,
Alexey Frunze15958152017-02-09 19:08:30 -0800896 nullptr);
897 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
898 } else {
899 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
900 __ LoadConst32(calling_convention.GetRegisterAt(2), offset_);
901 }
902 mips_codegen->InvokeRuntime(kQuickReadBarrierSlow,
903 instruction_,
904 instruction_->GetDexPc(),
905 this);
906 CheckEntrypointTypes<
907 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
Lena Djokic8098da92017-06-28 12:07:50 +0200908 mips_codegen->MoveLocation(out_,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100909 calling_convention.GetReturnLocation(DataType::Type::kReference),
910 DataType::Type::kReference);
Alexey Frunze15958152017-02-09 19:08:30 -0800911
912 RestoreLiveRegisters(codegen, locations);
913 __ B(GetExitLabel());
914 }
915
916 const char* GetDescription() const OVERRIDE { return "ReadBarrierForHeapReferenceSlowPathMIPS"; }
917
918 private:
919 Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
920 size_t ref = static_cast<int>(ref_.AsRegister<Register>());
921 size_t obj = static_cast<int>(obj_.AsRegister<Register>());
922 for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
923 if (i != ref &&
924 i != obj &&
925 !codegen->IsCoreCalleeSaveRegister(i) &&
926 !codegen->IsBlockedCoreRegister(i)) {
927 return static_cast<Register>(i);
928 }
929 }
930 // We shall never fail to find a free caller-save register, as
931 // there are more than two core caller-save registers on MIPS
932 // (meaning it is possible to find one which is different from
933 // `ref` and `obj`).
934 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
935 LOG(FATAL) << "Could not find a free caller-save register";
936 UNREACHABLE();
937 }
938
939 const Location out_;
940 const Location ref_;
941 const Location obj_;
942 const uint32_t offset_;
943 // An additional location containing an index to an array.
944 // Only used for HArrayGet and the UnsafeGetObject &
945 // UnsafeGetObjectVolatile intrinsics.
946 const Location index_;
947
948 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathMIPS);
949};
950
951// Slow path generating a read barrier for a GC root.
952class ReadBarrierForRootSlowPathMIPS : public SlowPathCodeMIPS {
953 public:
954 ReadBarrierForRootSlowPathMIPS(HInstruction* instruction, Location out, Location root)
955 : SlowPathCodeMIPS(instruction), out_(out), root_(root) {
956 DCHECK(kEmitCompilerReadBarrier);
957 }
958
959 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
960 LocationSummary* locations = instruction_->GetLocations();
961 Register reg_out = out_.AsRegister<Register>();
962 DCHECK(locations->CanCall());
963 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
964 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
965 << "Unexpected instruction in read barrier for GC root slow path: "
966 << instruction_->DebugName();
967
968 __ Bind(GetEntryLabel());
969 SaveLiveRegisters(codegen, locations);
970
971 InvokeRuntimeCallingConvention calling_convention;
972 CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
Lena Djokic8098da92017-06-28 12:07:50 +0200973 mips_codegen->MoveLocation(Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
974 root_,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100975 DataType::Type::kReference);
Alexey Frunze15958152017-02-09 19:08:30 -0800976 mips_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
977 instruction_,
978 instruction_->GetDexPc(),
979 this);
980 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
Lena Djokic8098da92017-06-28 12:07:50 +0200981 mips_codegen->MoveLocation(out_,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100982 calling_convention.GetReturnLocation(DataType::Type::kReference),
983 DataType::Type::kReference);
Alexey Frunze15958152017-02-09 19:08:30 -0800984
985 RestoreLiveRegisters(codegen, locations);
986 __ B(GetExitLabel());
987 }
988
989 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathMIPS"; }
990
991 private:
992 const Location out_;
993 const Location root_;
994
995 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathMIPS);
996};
997
Goran Jakovljevicf652cec2015-08-25 16:11:42 +0200998CodeGeneratorMIPS::CodeGeneratorMIPS(HGraph* graph,
999 const MipsInstructionSetFeatures& isa_features,
1000 const CompilerOptions& compiler_options,
1001 OptimizingCompilerStats* stats)
1002 : CodeGenerator(graph,
1003 kNumberOfCoreRegisters,
1004 kNumberOfFRegisters,
1005 kNumberOfRegisterPairs,
1006 ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
1007 arraysize(kCoreCalleeSaves)),
1008 ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
1009 arraysize(kFpuCalleeSaves)),
1010 compiler_options,
1011 stats),
1012 block_labels_(nullptr),
1013 location_builder_(graph, this),
1014 instruction_visitor_(graph, this),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001015 move_resolver_(graph->GetAllocator(), this),
1016 assembler_(graph->GetAllocator(), &isa_features),
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001017 isa_features_(isa_features),
Alexey Frunze06a46c42016-07-19 15:00:40 -07001018 uint32_literals_(std::less<uint32_t>(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001019 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1020 pc_relative_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1021 method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1022 pc_relative_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1023 type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1024 pc_relative_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1025 string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1026 jit_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1027 jit_class_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Alexey Frunze06a46c42016-07-19 15:00:40 -07001028 clobbered_ra_(false) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001029 // Save RA (containing the return address) to mimic Quick.
1030 AddAllocatedRegister(Location::RegisterLocation(RA));
1031}
1032
1033#undef __
Roland Levillain7cbd27f2016-08-11 23:53:33 +01001034// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
1035#define __ down_cast<MipsAssembler*>(GetAssembler())-> // NOLINT
Andreas Gampe542451c2016-07-26 09:02:02 -07001036#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, x).Int32Value()
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001037
1038void CodeGeneratorMIPS::Finalize(CodeAllocator* allocator) {
1039 // Ensure that we fix up branches.
1040 __ FinalizeCode();
1041
1042 // Adjust native pc offsets in stack maps.
Vladimir Marko174b2e22017-10-12 13:34:49 +01001043 StackMapStream* stack_map_stream = GetStackMapStream();
1044 for (size_t i = 0, num = stack_map_stream->GetNumberOfStackMaps(); i != num; ++i) {
Mathieu Chartiera2f526f2017-01-19 14:48:48 -08001045 uint32_t old_position =
Vladimir Marko33bff252017-11-01 14:35:42 +00001046 stack_map_stream->GetStackMap(i).native_pc_code_offset.Uint32Value(InstructionSet::kMips);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001047 uint32_t new_position = __ GetAdjustedPosition(old_position);
1048 DCHECK_GE(new_position, old_position);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001049 stack_map_stream->SetStackMapNativePcOffset(i, new_position);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001050 }
1051
1052 // Adjust pc offsets for the disassembly information.
1053 if (disasm_info_ != nullptr) {
1054 GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval();
1055 frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start);
1056 frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end);
1057 for (auto& it : *disasm_info_->GetInstructionIntervals()) {
1058 it.second.start = __ GetAdjustedPosition(it.second.start);
1059 it.second.end = __ GetAdjustedPosition(it.second.end);
1060 }
1061 for (auto& it : *disasm_info_->GetSlowPathIntervals()) {
1062 it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start);
1063 it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end);
1064 }
1065 }
1066
1067 CodeGenerator::Finalize(allocator);
1068}
1069
1070MipsAssembler* ParallelMoveResolverMIPS::GetAssembler() const {
1071 return codegen_->GetAssembler();
1072}
1073
1074void ParallelMoveResolverMIPS::EmitMove(size_t index) {
1075 DCHECK_LT(index, moves_.size());
1076 MoveOperands* move = moves_[index];
1077 codegen_->MoveLocation(move->GetDestination(), move->GetSource(), move->GetType());
1078}
1079
1080void ParallelMoveResolverMIPS::EmitSwap(size_t index) {
1081 DCHECK_LT(index, moves_.size());
1082 MoveOperands* move = moves_[index];
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001083 DataType::Type type = move->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001084 Location loc1 = move->GetDestination();
1085 Location loc2 = move->GetSource();
1086
1087 DCHECK(!loc1.IsConstant());
1088 DCHECK(!loc2.IsConstant());
1089
1090 if (loc1.Equals(loc2)) {
1091 return;
1092 }
1093
1094 if (loc1.IsRegister() && loc2.IsRegister()) {
1095 // Swap 2 GPRs.
1096 Register r1 = loc1.AsRegister<Register>();
1097 Register r2 = loc2.AsRegister<Register>();
1098 __ Move(TMP, r2);
1099 __ Move(r2, r1);
1100 __ Move(r1, TMP);
1101 } else if (loc1.IsFpuRegister() && loc2.IsFpuRegister()) {
Goran Jakovljevice7de5ec2017-12-14 10:25:20 +01001102 if (codegen_->GetGraph()->HasSIMD()) {
1103 __ MoveV(static_cast<VectorRegister>(FTMP), VectorRegisterFrom(loc1));
1104 __ MoveV(VectorRegisterFrom(loc1), VectorRegisterFrom(loc2));
1105 __ MoveV(VectorRegisterFrom(loc2), static_cast<VectorRegister>(FTMP));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001106 } else {
Goran Jakovljevice7de5ec2017-12-14 10:25:20 +01001107 FRegister f1 = loc1.AsFpuRegister<FRegister>();
1108 FRegister f2 = loc2.AsFpuRegister<FRegister>();
1109 if (type == DataType::Type::kFloat32) {
1110 __ MovS(FTMP, f2);
1111 __ MovS(f2, f1);
1112 __ MovS(f1, FTMP);
1113 } else {
1114 DCHECK_EQ(type, DataType::Type::kFloat64);
1115 __ MovD(FTMP, f2);
1116 __ MovD(f2, f1);
1117 __ MovD(f1, FTMP);
1118 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001119 }
1120 } else if ((loc1.IsRegister() && loc2.IsFpuRegister()) ||
1121 (loc1.IsFpuRegister() && loc2.IsRegister())) {
1122 // Swap FPR and GPR.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001123 DCHECK_EQ(type, DataType::Type::kFloat32); // Can only swap a float.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001124 FRegister f1 = loc1.IsFpuRegister() ? loc1.AsFpuRegister<FRegister>()
1125 : loc2.AsFpuRegister<FRegister>();
Goran Jakovljevic35dfcaa2016-09-22 09:26:01 +02001126 Register r2 = loc1.IsRegister() ? loc1.AsRegister<Register>() : loc2.AsRegister<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001127 __ Move(TMP, r2);
1128 __ Mfc1(r2, f1);
1129 __ Mtc1(TMP, f1);
1130 } else if (loc1.IsRegisterPair() && loc2.IsRegisterPair()) {
1131 // Swap 2 GPR register pairs.
1132 Register r1 = loc1.AsRegisterPairLow<Register>();
1133 Register r2 = loc2.AsRegisterPairLow<Register>();
1134 __ Move(TMP, r2);
1135 __ Move(r2, r1);
1136 __ Move(r1, TMP);
1137 r1 = loc1.AsRegisterPairHigh<Register>();
1138 r2 = loc2.AsRegisterPairHigh<Register>();
1139 __ Move(TMP, r2);
1140 __ Move(r2, r1);
1141 __ Move(r1, TMP);
1142 } else if ((loc1.IsRegisterPair() && loc2.IsFpuRegister()) ||
1143 (loc1.IsFpuRegister() && loc2.IsRegisterPair())) {
1144 // Swap FPR and GPR register pair.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001145 DCHECK_EQ(type, DataType::Type::kFloat64);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001146 FRegister f1 = loc1.IsFpuRegister() ? loc1.AsFpuRegister<FRegister>()
1147 : loc2.AsFpuRegister<FRegister>();
1148 Register r2_l = loc1.IsRegisterPair() ? loc1.AsRegisterPairLow<Register>()
1149 : loc2.AsRegisterPairLow<Register>();
1150 Register r2_h = loc1.IsRegisterPair() ? loc1.AsRegisterPairHigh<Register>()
1151 : loc2.AsRegisterPairHigh<Register>();
1152 // Use 2 temporary registers because we can't first swap the low 32 bits of an FPR and
1153 // then swap the high 32 bits of the same FPR. mtc1 makes the high 32 bits of an FPR
1154 // unpredictable and the following mfch1 will fail.
1155 __ Mfc1(TMP, f1);
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001156 __ MoveFromFpuHigh(AT, f1);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001157 __ Mtc1(r2_l, f1);
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001158 __ MoveToFpuHigh(r2_h, f1);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001159 __ Move(r2_l, TMP);
1160 __ Move(r2_h, AT);
1161 } else if (loc1.IsStackSlot() && loc2.IsStackSlot()) {
1162 Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ false);
1163 } else if (loc1.IsDoubleStackSlot() && loc2.IsDoubleStackSlot()) {
1164 Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ true);
Goran Jakovljevice7de5ec2017-12-14 10:25:20 +01001165 } else if (loc1.IsSIMDStackSlot() && loc2.IsSIMDStackSlot()) {
1166 ExchangeQuadSlots(loc1.GetStackIndex(), loc2.GetStackIndex());
David Brazdilcc0f3112016-01-28 17:14:52 +00001167 } else if ((loc1.IsRegister() && loc2.IsStackSlot()) ||
1168 (loc1.IsStackSlot() && loc2.IsRegister())) {
Goran Jakovljevic35dfcaa2016-09-22 09:26:01 +02001169 Register reg = loc1.IsRegister() ? loc1.AsRegister<Register>() : loc2.AsRegister<Register>();
1170 intptr_t offset = loc1.IsStackSlot() ? loc1.GetStackIndex() : loc2.GetStackIndex();
David Brazdilcc0f3112016-01-28 17:14:52 +00001171 __ Move(TMP, reg);
1172 __ LoadFromOffset(kLoadWord, reg, SP, offset);
1173 __ StoreToOffset(kStoreWord, TMP, SP, offset);
1174 } else if ((loc1.IsRegisterPair() && loc2.IsDoubleStackSlot()) ||
1175 (loc1.IsDoubleStackSlot() && loc2.IsRegisterPair())) {
1176 Register reg_l = loc1.IsRegisterPair() ? loc1.AsRegisterPairLow<Register>()
1177 : loc2.AsRegisterPairLow<Register>();
1178 Register reg_h = loc1.IsRegisterPair() ? loc1.AsRegisterPairHigh<Register>()
1179 : loc2.AsRegisterPairHigh<Register>();
Goran Jakovljevic35dfcaa2016-09-22 09:26:01 +02001180 intptr_t offset_l = loc1.IsDoubleStackSlot() ? loc1.GetStackIndex() : loc2.GetStackIndex();
David Brazdilcc0f3112016-01-28 17:14:52 +00001181 intptr_t offset_h = loc1.IsDoubleStackSlot() ? loc1.GetHighStackIndex(kMipsWordSize)
1182 : loc2.GetHighStackIndex(kMipsWordSize);
1183 __ Move(TMP, reg_l);
David Brazdilcc0f3112016-01-28 17:14:52 +00001184 __ LoadFromOffset(kLoadWord, reg_l, SP, offset_l);
David Brazdilcc0f3112016-01-28 17:14:52 +00001185 __ StoreToOffset(kStoreWord, TMP, SP, offset_l);
David Brazdil04d3e872016-01-29 09:50:09 +00001186 __ Move(TMP, reg_h);
1187 __ LoadFromOffset(kLoadWord, reg_h, SP, offset_h);
1188 __ StoreToOffset(kStoreWord, TMP, SP, offset_h);
Goran Jakovljevice7de5ec2017-12-14 10:25:20 +01001189 } else if ((loc1.IsFpuRegister() && loc2.IsSIMDStackSlot()) ||
1190 (loc1.IsSIMDStackSlot() && loc2.IsFpuRegister())) {
1191 Location fp_loc = loc1.IsFpuRegister() ? loc1 : loc2;
1192 intptr_t offset = loc1.IsFpuRegister() ? loc2.GetStackIndex() : loc1.GetStackIndex();
1193 __ MoveV(static_cast<VectorRegister>(FTMP), VectorRegisterFrom(fp_loc));
1194 __ LoadQFromOffset(fp_loc.AsFpuRegister<FRegister>(), SP, offset);
1195 __ StoreQToOffset(FTMP, SP, offset);
Goran Jakovljevic35dfcaa2016-09-22 09:26:01 +02001196 } else if (loc1.IsFpuRegister() || loc2.IsFpuRegister()) {
1197 FRegister reg = loc1.IsFpuRegister() ? loc1.AsFpuRegister<FRegister>()
1198 : loc2.AsFpuRegister<FRegister>();
1199 intptr_t offset = loc1.IsFpuRegister() ? loc2.GetStackIndex() : loc1.GetStackIndex();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001200 if (type == DataType::Type::kFloat32) {
Goran Jakovljevic35dfcaa2016-09-22 09:26:01 +02001201 __ MovS(FTMP, reg);
1202 __ LoadSFromOffset(reg, SP, offset);
1203 __ StoreSToOffset(FTMP, SP, offset);
1204 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001205 DCHECK_EQ(type, DataType::Type::kFloat64);
Goran Jakovljevic35dfcaa2016-09-22 09:26:01 +02001206 __ MovD(FTMP, reg);
1207 __ LoadDFromOffset(reg, SP, offset);
1208 __ StoreDToOffset(FTMP, SP, offset);
1209 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001210 } else {
1211 LOG(FATAL) << "Swap between " << loc1 << " and " << loc2 << " is unsupported";
1212 }
1213}
1214
1215void ParallelMoveResolverMIPS::RestoreScratch(int reg) {
1216 __ Pop(static_cast<Register>(reg));
1217}
1218
1219void ParallelMoveResolverMIPS::SpillScratch(int reg) {
1220 __ Push(static_cast<Register>(reg));
1221}
1222
1223void ParallelMoveResolverMIPS::Exchange(int index1, int index2, bool double_slot) {
1224 // Allocate a scratch register other than TMP, if available.
1225 // Else, spill V0 (arbitrary choice) and use it as a scratch register (it will be
1226 // automatically unspilled when the scratch scope object is destroyed).
1227 ScratchRegisterScope ensure_scratch(this, TMP, V0, codegen_->GetNumberOfCoreRegisters());
1228 // If V0 spills onto the stack, SP-relative offsets need to be adjusted.
Chris Larsen715f43e2017-10-23 11:00:32 -07001229 int stack_offset = ensure_scratch.IsSpilled() ? kStackAlignment : 0;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001230 for (int i = 0; i <= (double_slot ? 1 : 0); i++, stack_offset += kMipsWordSize) {
1231 __ LoadFromOffset(kLoadWord,
1232 Register(ensure_scratch.GetRegister()),
1233 SP,
1234 index1 + stack_offset);
1235 __ LoadFromOffset(kLoadWord,
1236 TMP,
1237 SP,
1238 index2 + stack_offset);
1239 __ StoreToOffset(kStoreWord,
1240 Register(ensure_scratch.GetRegister()),
1241 SP,
1242 index2 + stack_offset);
1243 __ StoreToOffset(kStoreWord, TMP, SP, index1 + stack_offset);
1244 }
1245}
1246
Goran Jakovljevice7de5ec2017-12-14 10:25:20 +01001247void ParallelMoveResolverMIPS::ExchangeQuadSlots(int index1, int index2) {
1248 __ LoadQFromOffset(FTMP, SP, index1);
1249 __ LoadQFromOffset(FTMP2, SP, index2);
1250 __ StoreQToOffset(FTMP, SP, index2);
1251 __ StoreQToOffset(FTMP2, SP, index1);
1252}
1253
Alexey Frunze73296a72016-06-03 22:51:46 -07001254void CodeGeneratorMIPS::ComputeSpillMask() {
1255 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
1256 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
1257 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
1258 // If there're FPU callee-saved registers and there's an odd number of GPR callee-saved
1259 // registers, include the ZERO register to force alignment of FPU callee-saved registers
1260 // within the stack frame.
1261 if ((fpu_spill_mask_ != 0) && (POPCOUNT(core_spill_mask_) % 2 != 0)) {
1262 core_spill_mask_ |= (1 << ZERO);
1263 }
Alexey Frunze58320ce2016-08-30 21:40:46 -07001264}
1265
1266bool CodeGeneratorMIPS::HasAllocatedCalleeSaveRegisters() const {
Alexey Frunze06a46c42016-07-19 15:00:40 -07001267 // If RA is clobbered by PC-relative operations on R2 and it's the only spilled register
Alexey Frunze58320ce2016-08-30 21:40:46 -07001268 // (this can happen in leaf methods), force CodeGenerator::InitializeCodeGeneration()
1269 // into the path that creates a stack frame so that RA can be explicitly saved and restored.
1270 // RA can't otherwise be saved/restored when it's the only spilled register.
Alexey Frunze58320ce2016-08-30 21:40:46 -07001271 return CodeGenerator::HasAllocatedCalleeSaveRegisters() || clobbered_ra_;
Alexey Frunze73296a72016-06-03 22:51:46 -07001272}
1273
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001274static dwarf::Reg DWARFReg(Register reg) {
1275 return dwarf::Reg::MipsCore(static_cast<int>(reg));
1276}
1277
1278// TODO: mapping of floating-point registers to DWARF.
1279
1280void CodeGeneratorMIPS::GenerateFrameEntry() {
1281 __ Bind(&frame_entry_label_);
1282
Nicolas Geoffray8d728322018-01-18 22:44:32 +00001283 if (GetCompilerOptions().CountHotnessInCompiledCode()) {
1284 LOG(WARNING) << "Unimplemented hotness update in mips backend";
1285 }
1286
Vladimir Marko33bff252017-11-01 14:35:42 +00001287 bool do_overflow_check =
1288 FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kMips) || !IsLeafMethod();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001289
1290 if (do_overflow_check) {
1291 __ LoadFromOffset(kLoadWord,
1292 ZERO,
1293 SP,
Vladimir Marko33bff252017-11-01 14:35:42 +00001294 -static_cast<int32_t>(GetStackOverflowReservedBytes(InstructionSet::kMips)));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001295 RecordPcInfo(nullptr, 0);
1296 }
1297
1298 if (HasEmptyFrame()) {
Alexey Frunze58320ce2016-08-30 21:40:46 -07001299 CHECK_EQ(fpu_spill_mask_, 0u);
1300 CHECK_EQ(core_spill_mask_, 1u << RA);
1301 CHECK(!clobbered_ra_);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001302 return;
1303 }
1304
1305 // Make sure the frame size isn't unreasonably large.
Vladimir Marko33bff252017-11-01 14:35:42 +00001306 if (GetFrameSize() > GetStackOverflowReservedBytes(InstructionSet::kMips)) {
1307 LOG(FATAL) << "Stack frame larger than "
1308 << GetStackOverflowReservedBytes(InstructionSet::kMips) << " bytes";
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001309 }
1310
1311 // Spill callee-saved registers.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001312
Alexey Frunze73296a72016-06-03 22:51:46 -07001313 uint32_t ofs = GetFrameSize();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001314 __ IncreaseFrameSize(ofs);
1315
Alexey Frunze73296a72016-06-03 22:51:46 -07001316 for (uint32_t mask = core_spill_mask_; mask != 0; ) {
1317 Register reg = static_cast<Register>(MostSignificantBit(mask));
1318 mask ^= 1u << reg;
1319 ofs -= kMipsWordSize;
1320 // The ZERO register is only included for alignment.
1321 if (reg != ZERO) {
1322 __ StoreToOffset(kStoreWord, reg, SP, ofs);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001323 __ cfi().RelOffset(DWARFReg(reg), ofs);
1324 }
1325 }
1326
Alexey Frunze73296a72016-06-03 22:51:46 -07001327 for (uint32_t mask = fpu_spill_mask_; mask != 0; ) {
1328 FRegister reg = static_cast<FRegister>(MostSignificantBit(mask));
1329 mask ^= 1u << reg;
1330 ofs -= kMipsDoublewordSize;
1331 __ StoreDToOffset(reg, SP, ofs);
1332 // TODO: __ cfi().RelOffset(DWARFReg(reg), ofs);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001333 }
1334
Nicolas Geoffray96eeb4e2016-10-12 22:03:31 +01001335 // Save the current method if we need it. Note that we do not
1336 // do this in HCurrentMethod, as the instruction might have been removed
1337 // in the SSA graph.
1338 if (RequiresCurrentMethod()) {
1339 __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
1340 }
Goran Jakovljevicc6418422016-12-05 16:31:55 +01001341
1342 if (GetGraph()->HasShouldDeoptimizeFlag()) {
1343 // Initialize should deoptimize flag to 0.
1344 __ StoreToOffset(kStoreWord, ZERO, SP, GetStackOffsetOfShouldDeoptimizeFlag());
1345 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001346}
1347
1348void CodeGeneratorMIPS::GenerateFrameExit() {
1349 __ cfi().RememberState();
1350
1351 if (!HasEmptyFrame()) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001352 // Restore callee-saved registers.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001353
Alexey Frunze73296a72016-06-03 22:51:46 -07001354 // For better instruction scheduling restore RA before other registers.
1355 uint32_t ofs = GetFrameSize();
1356 for (uint32_t mask = core_spill_mask_; mask != 0; ) {
1357 Register reg = static_cast<Register>(MostSignificantBit(mask));
1358 mask ^= 1u << reg;
1359 ofs -= kMipsWordSize;
1360 // The ZERO register is only included for alignment.
1361 if (reg != ZERO) {
1362 __ LoadFromOffset(kLoadWord, reg, SP, ofs);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001363 __ cfi().Restore(DWARFReg(reg));
1364 }
1365 }
1366
Alexey Frunze73296a72016-06-03 22:51:46 -07001367 for (uint32_t mask = fpu_spill_mask_; mask != 0; ) {
1368 FRegister reg = static_cast<FRegister>(MostSignificantBit(mask));
1369 mask ^= 1u << reg;
1370 ofs -= kMipsDoublewordSize;
1371 __ LoadDFromOffset(reg, SP, ofs);
1372 // TODO: __ cfi().Restore(DWARFReg(reg));
1373 }
1374
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001375 size_t frame_size = GetFrameSize();
1376 // Adjust the stack pointer in the delay slot if doing so doesn't break CFI.
1377 bool exchange = IsInt<16>(static_cast<int32_t>(frame_size));
1378 bool reordering = __ SetReorder(false);
1379 if (exchange) {
1380 __ Jr(RA);
1381 __ DecreaseFrameSize(frame_size); // Single instruction in delay slot.
1382 } else {
1383 __ DecreaseFrameSize(frame_size);
1384 __ Jr(RA);
1385 __ Nop(); // In delay slot.
1386 }
1387 __ SetReorder(reordering);
1388 } else {
1389 __ Jr(RA);
1390 __ NopIfNoReordering();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001391 }
1392
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001393 __ cfi().RestoreState();
1394 __ cfi().DefCFAOffset(GetFrameSize());
1395}
1396
1397void CodeGeneratorMIPS::Bind(HBasicBlock* block) {
1398 __ Bind(GetLabelOf(block));
1399}
1400
Lena Djokicca8c2952017-05-29 11:31:46 +02001401VectorRegister VectorRegisterFrom(Location location) {
1402 DCHECK(location.IsFpuRegister());
1403 return static_cast<VectorRegister>(location.AsFpuRegister<FRegister>());
1404}
1405
Lena Djokic8098da92017-06-28 12:07:50 +02001406void CodeGeneratorMIPS::MoveLocation(Location destination,
1407 Location source,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001408 DataType::Type dst_type) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001409 if (source.Equals(destination)) {
1410 return;
1411 }
1412
Lena Djokic8098da92017-06-28 12:07:50 +02001413 if (source.IsConstant()) {
1414 MoveConstant(destination, source.GetConstant());
1415 } else {
1416 if (destination.IsRegister()) {
1417 if (source.IsRegister()) {
1418 __ Move(destination.AsRegister<Register>(), source.AsRegister<Register>());
1419 } else if (source.IsFpuRegister()) {
1420 __ Mfc1(destination.AsRegister<Register>(), source.AsFpuRegister<FRegister>());
1421 } else {
1422 DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001423 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex());
Lena Djokic8098da92017-06-28 12:07:50 +02001424 }
1425 } else if (destination.IsRegisterPair()) {
1426 if (source.IsRegisterPair()) {
1427 __ Move(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
1428 __ Move(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
1429 } else if (source.IsFpuRegister()) {
1430 Register dst_high = destination.AsRegisterPairHigh<Register>();
1431 Register dst_low = destination.AsRegisterPairLow<Register>();
1432 FRegister src = source.AsFpuRegister<FRegister>();
1433 __ Mfc1(dst_low, src);
1434 __ MoveFromFpuHigh(dst_high, src);
1435 } else {
Alexey Frunze4147fcc2017-06-17 19:57:27 -07001436 DCHECK(source.IsDoubleStackSlot())
1437 << "Cannot move from " << source << " to " << destination;
Lena Djokic8098da92017-06-28 12:07:50 +02001438 int32_t off = source.GetStackIndex();
1439 Register r = destination.AsRegisterPairLow<Register>();
1440 __ LoadFromOffset(kLoadDoubleword, r, SP, off);
1441 }
1442 } else if (destination.IsFpuRegister()) {
1443 if (source.IsRegister()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001444 DCHECK(!DataType::Is64BitType(dst_type));
Lena Djokic8098da92017-06-28 12:07:50 +02001445 __ Mtc1(source.AsRegister<Register>(), destination.AsFpuRegister<FRegister>());
1446 } else if (source.IsRegisterPair()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001447 DCHECK(DataType::Is64BitType(dst_type));
Lena Djokic8098da92017-06-28 12:07:50 +02001448 FRegister dst = destination.AsFpuRegister<FRegister>();
1449 Register src_high = source.AsRegisterPairHigh<Register>();
1450 Register src_low = source.AsRegisterPairLow<Register>();
1451 __ Mtc1(src_low, dst);
1452 __ MoveToFpuHigh(src_high, dst);
1453 } else if (source.IsFpuRegister()) {
Lena Djokicca8c2952017-05-29 11:31:46 +02001454 if (GetGraph()->HasSIMD()) {
1455 __ MoveV(VectorRegisterFrom(destination),
1456 VectorRegisterFrom(source));
Lena Djokic8098da92017-06-28 12:07:50 +02001457 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001458 if (DataType::Is64BitType(dst_type)) {
Lena Djokicca8c2952017-05-29 11:31:46 +02001459 __ MovD(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
1460 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001461 DCHECK_EQ(dst_type, DataType::Type::kFloat32);
Lena Djokicca8c2952017-05-29 11:31:46 +02001462 __ MovS(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
1463 }
Lena Djokic8098da92017-06-28 12:07:50 +02001464 }
Lena Djokicca8c2952017-05-29 11:31:46 +02001465 } else if (source.IsSIMDStackSlot()) {
1466 __ LoadQFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
Lena Djokic8098da92017-06-28 12:07:50 +02001467 } else if (source.IsDoubleStackSlot()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001468 DCHECK(DataType::Is64BitType(dst_type));
Lena Djokic8098da92017-06-28 12:07:50 +02001469 __ LoadDFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
1470 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001471 DCHECK(!DataType::Is64BitType(dst_type));
Lena Djokic8098da92017-06-28 12:07:50 +02001472 DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
1473 __ LoadSFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
1474 }
Lena Djokicca8c2952017-05-29 11:31:46 +02001475 } else if (destination.IsSIMDStackSlot()) {
1476 if (source.IsFpuRegister()) {
1477 __ StoreQToOffset(source.AsFpuRegister<FRegister>(), SP, destination.GetStackIndex());
1478 } else {
1479 DCHECK(source.IsSIMDStackSlot());
1480 __ LoadQFromOffset(FTMP, SP, source.GetStackIndex());
1481 __ StoreQToOffset(FTMP, SP, destination.GetStackIndex());
1482 }
Lena Djokic8098da92017-06-28 12:07:50 +02001483 } else if (destination.IsDoubleStackSlot()) {
1484 int32_t dst_offset = destination.GetStackIndex();
1485 if (source.IsRegisterPair()) {
1486 __ StoreToOffset(kStoreDoubleword, source.AsRegisterPairLow<Register>(), SP, dst_offset);
1487 } else if (source.IsFpuRegister()) {
1488 __ StoreDToOffset(source.AsFpuRegister<FRegister>(), SP, dst_offset);
1489 } else {
Alexey Frunze4147fcc2017-06-17 19:57:27 -07001490 DCHECK(source.IsDoubleStackSlot())
1491 << "Cannot move from " << source << " to " << destination;
Lena Djokic8098da92017-06-28 12:07:50 +02001492 __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex());
1493 __ StoreToOffset(kStoreWord, TMP, SP, dst_offset);
1494 __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex() + 4);
1495 __ StoreToOffset(kStoreWord, TMP, SP, dst_offset + 4);
1496 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001497 } else {
Lena Djokic8098da92017-06-28 12:07:50 +02001498 DCHECK(destination.IsStackSlot()) << destination;
1499 int32_t dst_offset = destination.GetStackIndex();
1500 if (source.IsRegister()) {
1501 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, dst_offset);
1502 } else if (source.IsFpuRegister()) {
1503 __ StoreSToOffset(source.AsFpuRegister<FRegister>(), SP, dst_offset);
1504 } else {
1505 DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
1506 __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex());
1507 __ StoreToOffset(kStoreWord, TMP, SP, dst_offset);
1508 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001509 }
1510 }
1511}
1512
1513void CodeGeneratorMIPS::MoveConstant(Location destination, HConstant* c) {
1514 if (c->IsIntConstant() || c->IsNullConstant()) {
1515 // Move 32 bit constant.
1516 int32_t value = GetInt32ValueOf(c);
1517 if (destination.IsRegister()) {
1518 Register dst = destination.AsRegister<Register>();
1519 __ LoadConst32(dst, value);
1520 } else {
1521 DCHECK(destination.IsStackSlot())
1522 << "Cannot move " << c->DebugName() << " to " << destination;
Alexey Frunzef58b2482016-09-02 22:14:06 -07001523 __ StoreConstToOffset(kStoreWord, value, SP, destination.GetStackIndex(), TMP);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001524 }
1525 } else if (c->IsLongConstant()) {
1526 // Move 64 bit constant.
1527 int64_t value = GetInt64ValueOf(c);
1528 if (destination.IsRegisterPair()) {
1529 Register r_h = destination.AsRegisterPairHigh<Register>();
1530 Register r_l = destination.AsRegisterPairLow<Register>();
1531 __ LoadConst64(r_h, r_l, value);
1532 } else {
1533 DCHECK(destination.IsDoubleStackSlot())
1534 << "Cannot move " << c->DebugName() << " to " << destination;
Alexey Frunzef58b2482016-09-02 22:14:06 -07001535 __ StoreConstToOffset(kStoreDoubleword, value, SP, destination.GetStackIndex(), TMP);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001536 }
1537 } else if (c->IsFloatConstant()) {
1538 // Move 32 bit float constant.
1539 int32_t value = GetInt32ValueOf(c);
1540 if (destination.IsFpuRegister()) {
1541 __ LoadSConst32(destination.AsFpuRegister<FRegister>(), value, TMP);
1542 } else {
1543 DCHECK(destination.IsStackSlot())
1544 << "Cannot move " << c->DebugName() << " to " << destination;
Alexey Frunzef58b2482016-09-02 22:14:06 -07001545 __ StoreConstToOffset(kStoreWord, value, SP, destination.GetStackIndex(), TMP);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001546 }
1547 } else {
1548 // Move 64 bit double constant.
1549 DCHECK(c->IsDoubleConstant()) << c->DebugName();
1550 int64_t value = GetInt64ValueOf(c);
1551 if (destination.IsFpuRegister()) {
1552 FRegister fd = destination.AsFpuRegister<FRegister>();
1553 __ LoadDConst64(fd, value, TMP);
1554 } else {
1555 DCHECK(destination.IsDoubleStackSlot())
1556 << "Cannot move " << c->DebugName() << " to " << destination;
Alexey Frunzef58b2482016-09-02 22:14:06 -07001557 __ StoreConstToOffset(kStoreDoubleword, value, SP, destination.GetStackIndex(), TMP);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001558 }
1559 }
1560}
1561
1562void CodeGeneratorMIPS::MoveConstant(Location destination, int32_t value) {
1563 DCHECK(destination.IsRegister());
1564 Register dst = destination.AsRegister<Register>();
1565 __ LoadConst32(dst, value);
1566}
1567
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001568void CodeGeneratorMIPS::AddLocationAsTemp(Location location, LocationSummary* locations) {
1569 if (location.IsRegister()) {
1570 locations->AddTemp(location);
Alexey Frunzec9e94f32015-10-26 16:11:39 -07001571 } else if (location.IsRegisterPair()) {
1572 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>()));
1573 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>()));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001574 } else {
1575 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
1576 }
1577}
1578
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01001579template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
Vladimir Markoaad75c62016-10-03 08:46:48 +00001580inline void CodeGeneratorMIPS::EmitPcRelativeLinkerPatches(
1581 const ArenaDeque<PcRelativePatchInfo>& infos,
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01001582 ArenaVector<linker::LinkerPatch>* linker_patches) {
Vladimir Markoaad75c62016-10-03 08:46:48 +00001583 for (const PcRelativePatchInfo& info : infos) {
1584 const DexFile& dex_file = info.target_dex_file;
1585 size_t offset_or_index = info.offset_or_index;
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001586 DCHECK(info.label.IsBound());
1587 uint32_t literal_offset = __ GetLabelLocation(&info.label);
Vladimir Markoaad75c62016-10-03 08:46:48 +00001588 // On R2 we use HMipsComputeBaseMethodAddress and patch relative to
1589 // the assembler's base label used for PC-relative addressing.
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001590 const PcRelativePatchInfo& info_high = info.patch_info_high ? *info.patch_info_high : info;
1591 uint32_t pc_rel_offset = info_high.pc_rel_label.IsBound()
1592 ? __ GetLabelLocation(&info_high.pc_rel_label)
Vladimir Markoaad75c62016-10-03 08:46:48 +00001593 : __ GetPcRelBaseLabelLocation();
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001594 linker_patches->push_back(Factory(literal_offset, &dex_file, pc_rel_offset, offset_or_index));
Vladimir Markoaad75c62016-10-03 08:46:48 +00001595 }
1596}
1597
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01001598void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001599 DCHECK(linker_patches->empty());
1600 size_t size =
Vladimir Marko65979462017-05-19 17:25:12 +01001601 pc_relative_method_patches_.size() +
Vladimir Marko0eb882b2017-05-15 13:39:18 +01001602 method_bss_entry_patches_.size() +
Alexey Frunze06a46c42016-07-19 15:00:40 -07001603 pc_relative_type_patches_.size() +
Vladimir Marko65979462017-05-19 17:25:12 +01001604 type_bss_entry_patches_.size() +
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01001605 pc_relative_string_patches_.size() +
1606 string_bss_entry_patches_.size();
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001607 linker_patches->reserve(size);
Vladimir Marko65979462017-05-19 17:25:12 +01001608 if (GetCompilerOptions().IsBootImage()) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01001609 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
1610 pc_relative_method_patches_, linker_patches);
1611 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
1612 pc_relative_type_patches_, linker_patches);
1613 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
1614 pc_relative_string_patches_, linker_patches);
Vladimir Marko65979462017-05-19 17:25:12 +01001615 } else {
1616 DCHECK(pc_relative_method_patches_.empty());
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01001617 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>(
1618 pc_relative_type_patches_, linker_patches);
1619 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>(
1620 pc_relative_string_patches_, linker_patches);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001621 }
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01001622 EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
1623 method_bss_entry_patches_, linker_patches);
1624 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
1625 type_bss_entry_patches_, linker_patches);
1626 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
1627 string_bss_entry_patches_, linker_patches);
Vladimir Marko1998cd02017-01-13 13:02:58 +00001628 DCHECK_EQ(size, linker_patches->size());
Alexey Frunze06a46c42016-07-19 15:00:40 -07001629}
1630
Vladimir Marko65979462017-05-19 17:25:12 +01001631CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeMethodPatch(
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001632 MethodReference target_method,
1633 const PcRelativePatchInfo* info_high) {
Vladimir Marko65979462017-05-19 17:25:12 +01001634 return NewPcRelativePatch(*target_method.dex_file,
Mathieu Chartierfc8b4222017-09-17 13:44:24 -07001635 target_method.index,
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001636 info_high,
Vladimir Marko65979462017-05-19 17:25:12 +01001637 &pc_relative_method_patches_);
Alexey Frunze06a46c42016-07-19 15:00:40 -07001638}
1639
Vladimir Marko0eb882b2017-05-15 13:39:18 +01001640CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewMethodBssEntryPatch(
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001641 MethodReference target_method,
1642 const PcRelativePatchInfo* info_high) {
Vladimir Marko0eb882b2017-05-15 13:39:18 +01001643 return NewPcRelativePatch(*target_method.dex_file,
Mathieu Chartierfc8b4222017-09-17 13:44:24 -07001644 target_method.index,
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001645 info_high,
Vladimir Marko0eb882b2017-05-15 13:39:18 +01001646 &method_bss_entry_patches_);
1647}
1648
Alexey Frunze06a46c42016-07-19 15:00:40 -07001649CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeTypePatch(
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001650 const DexFile& dex_file,
1651 dex::TypeIndex type_index,
1652 const PcRelativePatchInfo* info_high) {
1653 return NewPcRelativePatch(dex_file, type_index.index_, info_high, &pc_relative_type_patches_);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001654}
1655
Vladimir Marko1998cd02017-01-13 13:02:58 +00001656CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewTypeBssEntryPatch(
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001657 const DexFile& dex_file,
1658 dex::TypeIndex type_index,
1659 const PcRelativePatchInfo* info_high) {
1660 return NewPcRelativePatch(dex_file, type_index.index_, info_high, &type_bss_entry_patches_);
Vladimir Marko1998cd02017-01-13 13:02:58 +00001661}
1662
Vladimir Marko65979462017-05-19 17:25:12 +01001663CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeStringPatch(
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001664 const DexFile& dex_file,
1665 dex::StringIndex string_index,
1666 const PcRelativePatchInfo* info_high) {
1667 return NewPcRelativePatch(dex_file, string_index.index_, info_high, &pc_relative_string_patches_);
Vladimir Marko65979462017-05-19 17:25:12 +01001668}
1669
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01001670CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewStringBssEntryPatch(
1671 const DexFile& dex_file,
1672 dex::StringIndex string_index,
1673 const PcRelativePatchInfo* info_high) {
1674 return NewPcRelativePatch(dex_file, string_index.index_, info_high, &string_bss_entry_patches_);
1675}
1676
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001677CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativePatch(
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001678 const DexFile& dex_file,
1679 uint32_t offset_or_index,
1680 const PcRelativePatchInfo* info_high,
1681 ArenaDeque<PcRelativePatchInfo>* patches) {
1682 patches->emplace_back(dex_file, offset_or_index, info_high);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001683 return &patches->back();
1684}
1685
Alexey Frunze06a46c42016-07-19 15:00:40 -07001686Literal* CodeGeneratorMIPS::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
1687 return map->GetOrCreate(
1688 value,
1689 [this, value]() { return __ NewLiteral<uint32_t>(value); });
1690}
1691
Alexey Frunze06a46c42016-07-19 15:00:40 -07001692Literal* CodeGeneratorMIPS::DeduplicateBootImageAddressLiteral(uint32_t address) {
Richard Uhlerc52f3032017-03-02 13:45:45 +00001693 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
Alexey Frunze06a46c42016-07-19 15:00:40 -07001694}
1695
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001696void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high,
Alexey Frunze6b892cd2017-01-03 17:11:38 -08001697 Register out,
Alexey Frunzea663d9d2017-07-31 18:43:18 -07001698 Register base) {
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001699 DCHECK(!info_high->patch_info_high);
Alexey Frunze6079dca2017-05-28 19:10:28 -07001700 DCHECK_NE(out, base);
Alexey Frunzea663d9d2017-07-31 18:43:18 -07001701 bool reordering = __ SetReorder(false);
Vladimir Markoaad75c62016-10-03 08:46:48 +00001702 if (GetInstructionSetFeatures().IsR6()) {
1703 DCHECK_EQ(base, ZERO);
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001704 __ Bind(&info_high->label);
1705 __ Bind(&info_high->pc_rel_label);
Alexey Frunze6b892cd2017-01-03 17:11:38 -08001706 // Add the high half of a 32-bit offset to PC.
Vladimir Markoaad75c62016-10-03 08:46:48 +00001707 __ Auipc(out, /* placeholder */ 0x1234);
Alexey Frunzea663d9d2017-07-31 18:43:18 -07001708 __ SetReorder(reordering);
Vladimir Markoaad75c62016-10-03 08:46:48 +00001709 } else {
1710 // If base is ZERO, emit NAL to obtain the actual base.
1711 if (base == ZERO) {
1712 // Generate a dummy PC-relative call to obtain PC.
1713 __ Nal();
1714 }
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001715 __ Bind(&info_high->label);
Vladimir Markoaad75c62016-10-03 08:46:48 +00001716 __ Lui(out, /* placeholder */ 0x1234);
1717 // If we emitted the NAL, bind the pc_rel_label, otherwise base is a register holding
1718 // the HMipsComputeBaseMethodAddress which has its own label stored in MipsAssembler.
1719 if (base == ZERO) {
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001720 __ Bind(&info_high->pc_rel_label);
Vladimir Markoaad75c62016-10-03 08:46:48 +00001721 }
Alexey Frunzea663d9d2017-07-31 18:43:18 -07001722 __ SetReorder(reordering);
Alexey Frunze6b892cd2017-01-03 17:11:38 -08001723 // Add the high half of a 32-bit offset to PC.
Vladimir Markoaad75c62016-10-03 08:46:48 +00001724 __ Addu(out, out, (base == ZERO) ? RA : base);
1725 }
Alexey Frunze5fa5c042017-06-01 21:07:52 -07001726 // A following instruction will add the sign-extended low half of the 32-bit
Alexey Frunze6b892cd2017-01-03 17:11:38 -08001727 // offset to `out` (e.g. lw, jialc, addiu).
Vladimir Markoaad75c62016-10-03 08:46:48 +00001728}
1729
Alexey Frunze627c1a02017-01-30 19:28:14 -08001730CodeGeneratorMIPS::JitPatchInfo* CodeGeneratorMIPS::NewJitRootStringPatch(
1731 const DexFile& dex_file,
Vladimir Marko174b2e22017-10-12 13:34:49 +01001732 dex::StringIndex string_index,
Alexey Frunze627c1a02017-01-30 19:28:14 -08001733 Handle<mirror::String> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001734 ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
1735 jit_string_patches_.emplace_back(dex_file, string_index.index_);
Alexey Frunze627c1a02017-01-30 19:28:14 -08001736 return &jit_string_patches_.back();
1737}
1738
1739CodeGeneratorMIPS::JitPatchInfo* CodeGeneratorMIPS::NewJitRootClassPatch(
1740 const DexFile& dex_file,
Vladimir Marko174b2e22017-10-12 13:34:49 +01001741 dex::TypeIndex type_index,
Alexey Frunze627c1a02017-01-30 19:28:14 -08001742 Handle<mirror::Class> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001743 ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
1744 jit_class_patches_.emplace_back(dex_file, type_index.index_);
Alexey Frunze627c1a02017-01-30 19:28:14 -08001745 return &jit_class_patches_.back();
1746}
1747
1748void CodeGeneratorMIPS::PatchJitRootUse(uint8_t* code,
1749 const uint8_t* roots_data,
1750 const CodeGeneratorMIPS::JitPatchInfo& info,
1751 uint64_t index_in_table) const {
Alexey Frunze4147fcc2017-06-17 19:57:27 -07001752 uint32_t high_literal_offset = GetAssembler().GetLabelLocation(&info.high_label);
1753 uint32_t low_literal_offset = GetAssembler().GetLabelLocation(&info.low_label);
Alexey Frunze627c1a02017-01-30 19:28:14 -08001754 uintptr_t address =
1755 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
1756 uint32_t addr32 = dchecked_integral_cast<uint32_t>(address);
1757 // lui reg, addr32_high
Alexey Frunze4147fcc2017-06-17 19:57:27 -07001758 DCHECK_EQ(code[high_literal_offset + 0], 0x34);
1759 DCHECK_EQ(code[high_literal_offset + 1], 0x12);
1760 DCHECK_EQ((code[high_literal_offset + 2] & 0xE0), 0x00);
1761 DCHECK_EQ(code[high_literal_offset + 3], 0x3C);
Alexey Frunzec61c0762017-04-10 13:54:23 -07001762 // instr reg, reg, addr32_low
Alexey Frunze4147fcc2017-06-17 19:57:27 -07001763 DCHECK_EQ(code[low_literal_offset + 0], 0x78);
1764 DCHECK_EQ(code[low_literal_offset + 1], 0x56);
Alexey Frunzec61c0762017-04-10 13:54:23 -07001765 addr32 += (addr32 & 0x8000) << 1; // Account for sign extension in "instr reg, reg, addr32_low".
Alexey Frunze627c1a02017-01-30 19:28:14 -08001766 // lui reg, addr32_high
Alexey Frunze4147fcc2017-06-17 19:57:27 -07001767 code[high_literal_offset + 0] = static_cast<uint8_t>(addr32 >> 16);
1768 code[high_literal_offset + 1] = static_cast<uint8_t>(addr32 >> 24);
Alexey Frunzec61c0762017-04-10 13:54:23 -07001769 // instr reg, reg, addr32_low
Alexey Frunze4147fcc2017-06-17 19:57:27 -07001770 code[low_literal_offset + 0] = static_cast<uint8_t>(addr32 >> 0);
1771 code[low_literal_offset + 1] = static_cast<uint8_t>(addr32 >> 8);
Alexey Frunze627c1a02017-01-30 19:28:14 -08001772}
1773
1774void CodeGeneratorMIPS::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
1775 for (const JitPatchInfo& info : jit_string_patches_) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001776 StringReference string_reference(&info.target_dex_file, dex::StringIndex(info.index));
1777 uint64_t index_in_table = GetJitStringRootIndex(string_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01001778 PatchJitRootUse(code, roots_data, info, index_in_table);
Alexey Frunze627c1a02017-01-30 19:28:14 -08001779 }
1780 for (const JitPatchInfo& info : jit_class_patches_) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01001781 TypeReference type_reference(&info.target_dex_file, dex::TypeIndex(info.index));
1782 uint64_t index_in_table = GetJitClassRootIndex(type_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01001783 PatchJitRootUse(code, roots_data, info, index_in_table);
Alexey Frunze627c1a02017-01-30 19:28:14 -08001784 }
1785}
1786
Goran Jakovljevice114da22016-12-26 14:21:43 +01001787void CodeGeneratorMIPS::MarkGCCard(Register object,
1788 Register value,
1789 bool value_can_be_null) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001790 MipsLabel done;
1791 Register card = AT;
1792 Register temp = TMP;
Goran Jakovljevice114da22016-12-26 14:21:43 +01001793 if (value_can_be_null) {
1794 __ Beqz(value, &done);
1795 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001796 __ LoadFromOffset(kLoadWord,
1797 card,
1798 TR,
Andreas Gampe542451c2016-07-26 09:02:02 -07001799 Thread::CardTableOffset<kMipsPointerSize>().Int32Value());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001800 __ Srl(temp, object, gc::accounting::CardTable::kCardShift);
1801 __ Addu(temp, card, temp);
1802 __ Sb(card, temp, 0);
Goran Jakovljevice114da22016-12-26 14:21:43 +01001803 if (value_can_be_null) {
1804 __ Bind(&done);
1805 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001806}
1807
David Brazdil58282f42016-01-14 12:45:10 +00001808void CodeGeneratorMIPS::SetupBlockedRegisters() const {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001809 // ZERO, K0, K1, GP, SP, RA are always reserved and can't be allocated.
1810 blocked_core_registers_[ZERO] = true;
1811 blocked_core_registers_[K0] = true;
1812 blocked_core_registers_[K1] = true;
1813 blocked_core_registers_[GP] = true;
1814 blocked_core_registers_[SP] = true;
1815 blocked_core_registers_[RA] = true;
1816
1817 // AT and TMP(T8) are used as temporary/scratch registers
1818 // (similar to how AT is used by MIPS assemblers).
1819 blocked_core_registers_[AT] = true;
1820 blocked_core_registers_[TMP] = true;
1821 blocked_fpu_registers_[FTMP] = true;
1822
Goran Jakovljevice7de5ec2017-12-14 10:25:20 +01001823 if (GetInstructionSetFeatures().HasMsa()) {
1824 // To be used just for MSA instructions.
1825 blocked_fpu_registers_[FTMP2] = true;
1826 }
1827
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001828 // Reserve suspend and thread registers.
1829 blocked_core_registers_[S0] = true;
1830 blocked_core_registers_[TR] = true;
1831
1832 // Reserve T9 for function calls
1833 blocked_core_registers_[T9] = true;
1834
1835 // Reserve odd-numbered FPU registers.
1836 for (size_t i = 1; i < kNumberOfFRegisters; i += 2) {
1837 blocked_fpu_registers_[i] = true;
1838 }
1839
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02001840 if (GetGraph()->IsDebuggable()) {
1841 // Stubs do not save callee-save floating point registers. If the graph
1842 // is debuggable, we need to deal with these registers differently. For
1843 // now, just block them.
1844 for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
1845 blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
1846 }
1847 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001848}
1849
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001850size_t CodeGeneratorMIPS::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
1851 __ StoreToOffset(kStoreWord, Register(reg_id), SP, stack_index);
1852 return kMipsWordSize;
1853}
1854
1855size_t CodeGeneratorMIPS::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
1856 __ LoadFromOffset(kLoadWord, Register(reg_id), SP, stack_index);
1857 return kMipsWordSize;
1858}
1859
1860size_t CodeGeneratorMIPS::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
Lena Djokicca8c2952017-05-29 11:31:46 +02001861 if (GetGraph()->HasSIMD()) {
1862 __ StoreQToOffset(FRegister(reg_id), SP, stack_index);
1863 } else {
1864 __ StoreDToOffset(FRegister(reg_id), SP, stack_index);
1865 }
1866 return GetFloatingPointSpillSlotSize();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001867}
1868
1869size_t CodeGeneratorMIPS::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
Lena Djokicca8c2952017-05-29 11:31:46 +02001870 if (GetGraph()->HasSIMD()) {
1871 __ LoadQFromOffset(FRegister(reg_id), SP, stack_index);
1872 } else {
1873 __ LoadDFromOffset(FRegister(reg_id), SP, stack_index);
1874 }
1875 return GetFloatingPointSpillSlotSize();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001876}
1877
1878void CodeGeneratorMIPS::DumpCoreRegister(std::ostream& stream, int reg) const {
Vladimir Marko623a7a22016-02-02 18:14:52 +00001879 stream << Register(reg);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001880}
1881
1882void CodeGeneratorMIPS::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
Vladimir Marko623a7a22016-02-02 18:14:52 +00001883 stream << FRegister(reg);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001884}
1885
Serban Constantinescufca16662016-07-14 09:21:59 +01001886constexpr size_t kMipsDirectEntrypointRuntimeOffset = 16;
1887
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001888void CodeGeneratorMIPS::InvokeRuntime(QuickEntrypointEnum entrypoint,
1889 HInstruction* instruction,
1890 uint32_t dex_pc,
1891 SlowPathCode* slow_path) {
Alexandre Rames91a65162016-09-19 13:54:30 +01001892 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Alexey Frunze15958152017-02-09 19:08:30 -08001893 GenerateInvokeRuntime(GetThreadOffset<kMipsPointerSize>(entrypoint).Int32Value(),
1894 IsDirectEntrypoint(entrypoint));
1895 if (EntrypointRequiresStackMap(entrypoint)) {
1896 RecordPcInfo(instruction, dex_pc, slow_path);
1897 }
1898}
1899
1900void CodeGeneratorMIPS::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
1901 HInstruction* instruction,
1902 SlowPathCode* slow_path,
1903 bool direct) {
1904 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
1905 GenerateInvokeRuntime(entry_point_offset, direct);
1906}
1907
1908void CodeGeneratorMIPS::GenerateInvokeRuntime(int32_t entry_point_offset, bool direct) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001909 bool reordering = __ SetReorder(false);
Alexey Frunze15958152017-02-09 19:08:30 -08001910 __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001911 __ Jalr(T9);
Alexey Frunze15958152017-02-09 19:08:30 -08001912 if (direct) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001913 // Reserve argument space on stack (for $a0-$a3) for
1914 // entrypoints that directly reference native implementations.
1915 // Called function may use this space to store $a0-$a3 regs.
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001916 __ IncreaseFrameSize(kMipsDirectEntrypointRuntimeOffset); // Single instruction in delay slot.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001917 __ DecreaseFrameSize(kMipsDirectEntrypointRuntimeOffset);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001918 } else {
1919 __ Nop(); // In delay slot.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001920 }
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001921 __ SetReorder(reordering);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001922}
1923
1924void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path,
1925 Register class_reg) {
Vladimir Markodc682aa2018-01-04 18:42:57 +00001926 constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
1927 const size_t status_byte_offset =
1928 mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
1929 constexpr uint32_t shifted_initialized_value =
1930 enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte);
1931
1932 __ LoadFromOffset(kLoadUnsignedByte, TMP, class_reg, status_byte_offset);
1933 __ LoadConst32(AT, shifted_initialized_value);
Vladimir Marko2c64a832018-01-04 11:31:56 +00001934 __ Bltu(TMP, AT, slow_path->GetEntryLabel());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001935 // Even if the initialized flag is set, we need to ensure consistent memory ordering.
1936 __ Sync(0);
1937 __ Bind(slow_path->GetExitLabel());
1938}
1939
1940void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {
1941 __ Sync(0); // Only stype 0 is supported.
1942}
1943
1944void InstructionCodeGeneratorMIPS::GenerateSuspendCheck(HSuspendCheck* instruction,
1945 HBasicBlock* successor) {
1946 SuspendCheckSlowPathMIPS* slow_path =
Chris Larsena2045912017-11-02 12:39:54 -07001947 down_cast<SuspendCheckSlowPathMIPS*>(instruction->GetSlowPath());
1948
1949 if (slow_path == nullptr) {
1950 slow_path =
1951 new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathMIPS(instruction, successor);
1952 instruction->SetSlowPath(slow_path);
1953 codegen_->AddSlowPath(slow_path);
1954 if (successor != nullptr) {
1955 DCHECK(successor->IsLoopHeader());
1956 }
1957 } else {
1958 DCHECK_EQ(slow_path->GetSuccessor(), successor);
1959 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001960
1961 __ LoadFromOffset(kLoadUnsignedHalfword,
1962 TMP,
1963 TR,
Andreas Gampe542451c2016-07-26 09:02:02 -07001964 Thread::ThreadFlagsOffset<kMipsPointerSize>().Int32Value());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001965 if (successor == nullptr) {
1966 __ Bnez(TMP, slow_path->GetEntryLabel());
1967 __ Bind(slow_path->GetReturnLabel());
1968 } else {
1969 __ Beqz(TMP, codegen_->GetLabelOf(successor));
1970 __ B(slow_path->GetEntryLabel());
1971 // slow_path will return to GetLabelOf(successor).
1972 }
1973}
1974
1975InstructionCodeGeneratorMIPS::InstructionCodeGeneratorMIPS(HGraph* graph,
1976 CodeGeneratorMIPS* codegen)
Aart Bik42249c32016-01-07 15:33:50 -08001977 : InstructionCodeGenerator(graph, codegen),
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001978 assembler_(codegen->GetAssembler()),
1979 codegen_(codegen) {}
1980
1981void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) {
1982 DCHECK_EQ(instruction->InputCount(), 2U);
Vladimir Markoca6fff82017-10-03 14:49:14 +01001983 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001984 DataType::Type type = instruction->GetResultType();
Lena Djokic38530172017-11-16 11:11:50 +01001985 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001986 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001987 case DataType::Type::kInt32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001988 locations->SetInAt(0, Location::RequiresRegister());
1989 HInstruction* right = instruction->InputAt(1);
1990 bool can_use_imm = false;
1991 if (right->IsConstant()) {
1992 int32_t imm = CodeGenerator::GetInt32ValueOf(right->AsConstant());
1993 if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) {
1994 can_use_imm = IsUint<16>(imm);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02001995 } else {
Lena Djokic38530172017-11-16 11:11:50 +01001996 DCHECK(instruction->IsSub() || instruction->IsAdd());
1997 if (instruction->IsSub()) {
1998 imm = -imm;
1999 }
2000 if (isR6) {
2001 bool single_use = right->GetUses().HasExactlyOneElement();
2002 int16_t imm_high = High16Bits(imm);
2003 int16_t imm_low = Low16Bits(imm);
2004 if (imm_low < 0) {
2005 imm_high += 1;
2006 }
2007 can_use_imm = !((imm_high != 0) && (imm_low != 0)) || single_use;
2008 } else {
2009 can_use_imm = IsInt<16>(imm);
2010 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002011 }
2012 }
2013 if (can_use_imm)
2014 locations->SetInAt(1, Location::ConstantLocation(right->AsConstant()));
2015 else
2016 locations->SetInAt(1, Location::RequiresRegister());
2017 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2018 break;
2019 }
2020
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002021 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002022 locations->SetInAt(0, Location::RequiresRegister());
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002023 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2024 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002025 break;
2026 }
2027
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002028 case DataType::Type::kFloat32:
2029 case DataType::Type::kFloat64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002030 DCHECK(instruction->IsAdd() || instruction->IsSub());
2031 locations->SetInAt(0, Location::RequiresFpuRegister());
2032 locations->SetInAt(1, Location::RequiresFpuRegister());
2033 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2034 break;
2035
2036 default:
2037 LOG(FATAL) << "Unexpected " << instruction->DebugName() << " type " << type;
2038 }
2039}
2040
2041void InstructionCodeGeneratorMIPS::HandleBinaryOp(HBinaryOperation* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002042 DataType::Type type = instruction->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002043 LocationSummary* locations = instruction->GetLocations();
Lena Djokic38530172017-11-16 11:11:50 +01002044 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002045
2046 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002047 case DataType::Type::kInt32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002048 Register dst = locations->Out().AsRegister<Register>();
2049 Register lhs = locations->InAt(0).AsRegister<Register>();
2050 Location rhs_location = locations->InAt(1);
2051
2052 Register rhs_reg = ZERO;
2053 int32_t rhs_imm = 0;
2054 bool use_imm = rhs_location.IsConstant();
2055 if (use_imm) {
2056 rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
2057 } else {
2058 rhs_reg = rhs_location.AsRegister<Register>();
2059 }
2060
2061 if (instruction->IsAnd()) {
2062 if (use_imm)
2063 __ Andi(dst, lhs, rhs_imm);
2064 else
2065 __ And(dst, lhs, rhs_reg);
2066 } else if (instruction->IsOr()) {
2067 if (use_imm)
2068 __ Ori(dst, lhs, rhs_imm);
2069 else
2070 __ Or(dst, lhs, rhs_reg);
2071 } else if (instruction->IsXor()) {
2072 if (use_imm)
2073 __ Xori(dst, lhs, rhs_imm);
2074 else
2075 __ Xor(dst, lhs, rhs_reg);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002076 } else {
Lena Djokic38530172017-11-16 11:11:50 +01002077 DCHECK(instruction->IsAdd() || instruction->IsSub());
2078 if (use_imm) {
2079 if (instruction->IsSub()) {
2080 rhs_imm = -rhs_imm;
2081 }
2082 if (IsInt<16>(rhs_imm)) {
2083 __ Addiu(dst, lhs, rhs_imm);
2084 } else {
2085 DCHECK(isR6);
2086 int16_t rhs_imm_high = High16Bits(rhs_imm);
2087 int16_t rhs_imm_low = Low16Bits(rhs_imm);
2088 if (rhs_imm_low < 0) {
2089 rhs_imm_high += 1;
2090 }
2091 __ Aui(dst, lhs, rhs_imm_high);
2092 if (rhs_imm_low != 0) {
2093 __ Addiu(dst, dst, rhs_imm_low);
2094 }
2095 }
2096 } else if (instruction->IsAdd()) {
2097 __ Addu(dst, lhs, rhs_reg);
2098 } else {
2099 DCHECK(instruction->IsSub());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002100 __ Subu(dst, lhs, rhs_reg);
Lena Djokic38530172017-11-16 11:11:50 +01002101 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002102 }
2103 break;
2104 }
2105
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002106 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002107 Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
2108 Register dst_low = locations->Out().AsRegisterPairLow<Register>();
2109 Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
2110 Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002111 Location rhs_location = locations->InAt(1);
2112 bool use_imm = rhs_location.IsConstant();
2113 if (!use_imm) {
2114 Register rhs_high = rhs_location.AsRegisterPairHigh<Register>();
2115 Register rhs_low = rhs_location.AsRegisterPairLow<Register>();
2116 if (instruction->IsAnd()) {
2117 __ And(dst_low, lhs_low, rhs_low);
2118 __ And(dst_high, lhs_high, rhs_high);
2119 } else if (instruction->IsOr()) {
2120 __ Or(dst_low, lhs_low, rhs_low);
2121 __ Or(dst_high, lhs_high, rhs_high);
2122 } else if (instruction->IsXor()) {
2123 __ Xor(dst_low, lhs_low, rhs_low);
2124 __ Xor(dst_high, lhs_high, rhs_high);
2125 } else if (instruction->IsAdd()) {
2126 if (lhs_low == rhs_low) {
2127 // Special case for lhs = rhs and the sum potentially overwriting both lhs and rhs.
2128 __ Slt(TMP, lhs_low, ZERO);
2129 __ Addu(dst_low, lhs_low, rhs_low);
2130 } else {
2131 __ Addu(dst_low, lhs_low, rhs_low);
2132 // If the sum overwrites rhs, lhs remains unchanged, otherwise rhs remains unchanged.
2133 __ Sltu(TMP, dst_low, (dst_low == rhs_low) ? lhs_low : rhs_low);
2134 }
2135 __ Addu(dst_high, lhs_high, rhs_high);
2136 __ Addu(dst_high, dst_high, TMP);
2137 } else {
2138 DCHECK(instruction->IsSub());
2139 __ Sltu(TMP, lhs_low, rhs_low);
2140 __ Subu(dst_low, lhs_low, rhs_low);
2141 __ Subu(dst_high, lhs_high, rhs_high);
2142 __ Subu(dst_high, dst_high, TMP);
2143 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002144 } else {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002145 int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant());
2146 if (instruction->IsOr()) {
2147 uint32_t low = Low32Bits(value);
2148 uint32_t high = High32Bits(value);
2149 if (IsUint<16>(low)) {
2150 if (dst_low != lhs_low || low != 0) {
2151 __ Ori(dst_low, lhs_low, low);
2152 }
2153 } else {
2154 __ LoadConst32(TMP, low);
2155 __ Or(dst_low, lhs_low, TMP);
2156 }
2157 if (IsUint<16>(high)) {
2158 if (dst_high != lhs_high || high != 0) {
2159 __ Ori(dst_high, lhs_high, high);
2160 }
2161 } else {
2162 if (high != low) {
2163 __ LoadConst32(TMP, high);
2164 }
2165 __ Or(dst_high, lhs_high, TMP);
2166 }
2167 } else if (instruction->IsXor()) {
2168 uint32_t low = Low32Bits(value);
2169 uint32_t high = High32Bits(value);
2170 if (IsUint<16>(low)) {
2171 if (dst_low != lhs_low || low != 0) {
2172 __ Xori(dst_low, lhs_low, low);
2173 }
2174 } else {
2175 __ LoadConst32(TMP, low);
2176 __ Xor(dst_low, lhs_low, TMP);
2177 }
2178 if (IsUint<16>(high)) {
2179 if (dst_high != lhs_high || high != 0) {
2180 __ Xori(dst_high, lhs_high, high);
2181 }
2182 } else {
2183 if (high != low) {
2184 __ LoadConst32(TMP, high);
2185 }
2186 __ Xor(dst_high, lhs_high, TMP);
2187 }
2188 } else if (instruction->IsAnd()) {
2189 uint32_t low = Low32Bits(value);
2190 uint32_t high = High32Bits(value);
2191 if (IsUint<16>(low)) {
2192 __ Andi(dst_low, lhs_low, low);
2193 } else if (low != 0xFFFFFFFF) {
2194 __ LoadConst32(TMP, low);
2195 __ And(dst_low, lhs_low, TMP);
2196 } else if (dst_low != lhs_low) {
2197 __ Move(dst_low, lhs_low);
2198 }
2199 if (IsUint<16>(high)) {
2200 __ Andi(dst_high, lhs_high, high);
2201 } else if (high != 0xFFFFFFFF) {
2202 if (high != low) {
2203 __ LoadConst32(TMP, high);
2204 }
2205 __ And(dst_high, lhs_high, TMP);
2206 } else if (dst_high != lhs_high) {
2207 __ Move(dst_high, lhs_high);
2208 }
2209 } else {
2210 if (instruction->IsSub()) {
2211 value = -value;
2212 } else {
2213 DCHECK(instruction->IsAdd());
2214 }
2215 int32_t low = Low32Bits(value);
2216 int32_t high = High32Bits(value);
2217 if (IsInt<16>(low)) {
2218 if (dst_low != lhs_low || low != 0) {
2219 __ Addiu(dst_low, lhs_low, low);
2220 }
2221 if (low != 0) {
2222 __ Sltiu(AT, dst_low, low);
2223 }
2224 } else {
2225 __ LoadConst32(TMP, low);
2226 __ Addu(dst_low, lhs_low, TMP);
2227 __ Sltu(AT, dst_low, TMP);
2228 }
2229 if (IsInt<16>(high)) {
2230 if (dst_high != lhs_high || high != 0) {
2231 __ Addiu(dst_high, lhs_high, high);
2232 }
2233 } else {
2234 if (high != low) {
2235 __ LoadConst32(TMP, high);
2236 }
2237 __ Addu(dst_high, lhs_high, TMP);
2238 }
2239 if (low != 0) {
2240 __ Addu(dst_high, dst_high, AT);
2241 }
2242 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002243 }
2244 break;
2245 }
2246
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002247 case DataType::Type::kFloat32:
2248 case DataType::Type::kFloat64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002249 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
2250 FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
2251 FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
2252 if (instruction->IsAdd()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002253 if (type == DataType::Type::kFloat32) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002254 __ AddS(dst, lhs, rhs);
2255 } else {
2256 __ AddD(dst, lhs, rhs);
2257 }
2258 } else {
2259 DCHECK(instruction->IsSub());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002260 if (type == DataType::Type::kFloat32) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002261 __ SubS(dst, lhs, rhs);
2262 } else {
2263 __ SubD(dst, lhs, rhs);
2264 }
2265 }
2266 break;
2267 }
2268
2269 default:
2270 LOG(FATAL) << "Unexpected binary operation type " << type;
2271 }
2272}
2273
2274void LocationsBuilderMIPS::HandleShift(HBinaryOperation* instr) {
Alexey Frunze92d90602015-12-18 18:16:36 -08002275 DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr() || instr->IsRor());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002276
Vladimir Markoca6fff82017-10-03 14:49:14 +01002277 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instr);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002278 DataType::Type type = instr->GetResultType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002279 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002280 case DataType::Type::kInt32:
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002281 locations->SetInAt(0, Location::RequiresRegister());
2282 locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1)));
2283 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2284 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002285 case DataType::Type::kInt64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002286 locations->SetInAt(0, Location::RequiresRegister());
2287 locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1)));
2288 locations->SetOut(Location::RequiresRegister());
2289 break;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002290 default:
2291 LOG(FATAL) << "Unexpected shift type " << type;
2292 }
2293}
2294
2295static constexpr size_t kMipsBitsPerWord = kMipsWordSize * kBitsPerByte;
2296
2297void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) {
Alexey Frunze92d90602015-12-18 18:16:36 -08002298 DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr() || instr->IsRor());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002299 LocationSummary* locations = instr->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002300 DataType::Type type = instr->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002301
2302 Location rhs_location = locations->InAt(1);
2303 bool use_imm = rhs_location.IsConstant();
2304 Register rhs_reg = use_imm ? ZERO : rhs_location.AsRegister<Register>();
2305 int64_t rhs_imm = use_imm ? CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()) : 0;
Roland Levillain5b5b9312016-03-22 14:57:31 +00002306 const uint32_t shift_mask =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002307 (type == DataType::Type::kInt32) ? kMaxIntShiftDistance : kMaxLongShiftDistance;
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002308 const uint32_t shift_value = rhs_imm & shift_mask;
Alexey Frunze92d90602015-12-18 18:16:36 -08002309 // Are the INS (Insert Bit Field) and ROTR instructions supported?
2310 bool has_ins_rotr = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002311
2312 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002313 case DataType::Type::kInt32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002314 Register dst = locations->Out().AsRegister<Register>();
2315 Register lhs = locations->InAt(0).AsRegister<Register>();
2316 if (use_imm) {
Alexey Frunze92d90602015-12-18 18:16:36 -08002317 if (shift_value == 0) {
2318 if (dst != lhs) {
2319 __ Move(dst, lhs);
2320 }
2321 } else if (instr->IsShl()) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002322 __ Sll(dst, lhs, shift_value);
2323 } else if (instr->IsShr()) {
2324 __ Sra(dst, lhs, shift_value);
Alexey Frunze92d90602015-12-18 18:16:36 -08002325 } else if (instr->IsUShr()) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002326 __ Srl(dst, lhs, shift_value);
Alexey Frunze92d90602015-12-18 18:16:36 -08002327 } else {
2328 if (has_ins_rotr) {
2329 __ Rotr(dst, lhs, shift_value);
2330 } else {
2331 __ Sll(TMP, lhs, (kMipsBitsPerWord - shift_value) & shift_mask);
2332 __ Srl(dst, lhs, shift_value);
2333 __ Or(dst, dst, TMP);
2334 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002335 }
2336 } else {
2337 if (instr->IsShl()) {
2338 __ Sllv(dst, lhs, rhs_reg);
2339 } else if (instr->IsShr()) {
2340 __ Srav(dst, lhs, rhs_reg);
Alexey Frunze92d90602015-12-18 18:16:36 -08002341 } else if (instr->IsUShr()) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002342 __ Srlv(dst, lhs, rhs_reg);
Alexey Frunze92d90602015-12-18 18:16:36 -08002343 } else {
2344 if (has_ins_rotr) {
2345 __ Rotrv(dst, lhs, rhs_reg);
2346 } else {
2347 __ Subu(TMP, ZERO, rhs_reg);
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002348 // 32-bit shift instructions use the 5 least significant bits of the shift count, so
2349 // shifting by `-rhs_reg` is equivalent to shifting by `(32 - rhs_reg) & 31`. The case
2350 // when `rhs_reg & 31 == 0` is OK even though we don't shift `lhs` left all the way out
2351 // by 32, because the result in this case is computed as `(lhs >> 0) | (lhs << 0)`,
2352 // IOW, the OR'd values are equal.
Alexey Frunze92d90602015-12-18 18:16:36 -08002353 __ Sllv(TMP, lhs, TMP);
2354 __ Srlv(dst, lhs, rhs_reg);
2355 __ Or(dst, dst, TMP);
2356 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002357 }
2358 }
2359 break;
2360 }
2361
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002362 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002363 Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
2364 Register dst_low = locations->Out().AsRegisterPairLow<Register>();
2365 Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
2366 Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
2367 if (use_imm) {
2368 if (shift_value == 0) {
Lena Djokic8098da92017-06-28 12:07:50 +02002369 codegen_->MoveLocation(locations->Out(), locations->InAt(0), type);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002370 } else if (shift_value < kMipsBitsPerWord) {
Alexey Frunze92d90602015-12-18 18:16:36 -08002371 if (has_ins_rotr) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002372 if (instr->IsShl()) {
2373 __ Srl(dst_high, lhs_low, kMipsBitsPerWord - shift_value);
2374 __ Ins(dst_high, lhs_high, shift_value, kMipsBitsPerWord - shift_value);
2375 __ Sll(dst_low, lhs_low, shift_value);
2376 } else if (instr->IsShr()) {
2377 __ Srl(dst_low, lhs_low, shift_value);
2378 __ Ins(dst_low, lhs_high, kMipsBitsPerWord - shift_value, shift_value);
2379 __ Sra(dst_high, lhs_high, shift_value);
Alexey Frunze92d90602015-12-18 18:16:36 -08002380 } else if (instr->IsUShr()) {
2381 __ Srl(dst_low, lhs_low, shift_value);
2382 __ Ins(dst_low, lhs_high, kMipsBitsPerWord - shift_value, shift_value);
2383 __ Srl(dst_high, lhs_high, shift_value);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002384 } else {
2385 __ Srl(dst_low, lhs_low, shift_value);
2386 __ Ins(dst_low, lhs_high, kMipsBitsPerWord - shift_value, shift_value);
2387 __ Srl(dst_high, lhs_high, shift_value);
Alexey Frunze92d90602015-12-18 18:16:36 -08002388 __ Ins(dst_high, lhs_low, kMipsBitsPerWord - shift_value, shift_value);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002389 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002390 } else {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002391 if (instr->IsShl()) {
2392 __ Sll(dst_low, lhs_low, shift_value);
2393 __ Srl(TMP, lhs_low, kMipsBitsPerWord - shift_value);
2394 __ Sll(dst_high, lhs_high, shift_value);
2395 __ Or(dst_high, dst_high, TMP);
2396 } else if (instr->IsShr()) {
2397 __ Sra(dst_high, lhs_high, shift_value);
2398 __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value);
2399 __ Srl(dst_low, lhs_low, shift_value);
2400 __ Or(dst_low, dst_low, TMP);
Alexey Frunze92d90602015-12-18 18:16:36 -08002401 } else if (instr->IsUShr()) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002402 __ Srl(dst_high, lhs_high, shift_value);
2403 __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value);
2404 __ Srl(dst_low, lhs_low, shift_value);
2405 __ Or(dst_low, dst_low, TMP);
Alexey Frunze92d90602015-12-18 18:16:36 -08002406 } else {
2407 __ Srl(TMP, lhs_low, shift_value);
2408 __ Sll(dst_low, lhs_high, kMipsBitsPerWord - shift_value);
2409 __ Or(dst_low, dst_low, TMP);
2410 __ Srl(TMP, lhs_high, shift_value);
2411 __ Sll(dst_high, lhs_low, kMipsBitsPerWord - shift_value);
2412 __ Or(dst_high, dst_high, TMP);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08002413 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002414 }
2415 } else {
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002416 const uint32_t shift_value_high = shift_value - kMipsBitsPerWord;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002417 if (instr->IsShl()) {
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002418 __ Sll(dst_high, lhs_low, shift_value_high);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002419 __ Move(dst_low, ZERO);
2420 } else if (instr->IsShr()) {
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002421 __ Sra(dst_low, lhs_high, shift_value_high);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002422 __ Sra(dst_high, dst_low, kMipsBitsPerWord - 1);
Alexey Frunze92d90602015-12-18 18:16:36 -08002423 } else if (instr->IsUShr()) {
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002424 __ Srl(dst_low, lhs_high, shift_value_high);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002425 __ Move(dst_high, ZERO);
Alexey Frunze92d90602015-12-18 18:16:36 -08002426 } else {
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002427 if (shift_value == kMipsBitsPerWord) {
Alexey Frunze92d90602015-12-18 18:16:36 -08002428 // 64-bit rotation by 32 is just a swap.
2429 __ Move(dst_low, lhs_high);
2430 __ Move(dst_high, lhs_low);
2431 } else {
2432 if (has_ins_rotr) {
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002433 __ Srl(dst_low, lhs_high, shift_value_high);
2434 __ Ins(dst_low, lhs_low, kMipsBitsPerWord - shift_value_high, shift_value_high);
2435 __ Srl(dst_high, lhs_low, shift_value_high);
2436 __ Ins(dst_high, lhs_high, kMipsBitsPerWord - shift_value_high, shift_value_high);
Alexey Frunze92d90602015-12-18 18:16:36 -08002437 } else {
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002438 __ Sll(TMP, lhs_low, kMipsBitsPerWord - shift_value_high);
2439 __ Srl(dst_low, lhs_high, shift_value_high);
Alexey Frunze92d90602015-12-18 18:16:36 -08002440 __ Or(dst_low, dst_low, TMP);
Alexey Frunze0d9150b2016-01-13 16:24:25 -08002441 __ Sll(TMP, lhs_high, kMipsBitsPerWord - shift_value_high);
2442 __ Srl(dst_high, lhs_low, shift_value_high);
Alexey Frunze92d90602015-12-18 18:16:36 -08002443 __ Or(dst_high, dst_high, TMP);
2444 }
2445 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002446 }
2447 }
2448 } else {
Chris Larsen3e5fecd2017-11-09 14:21:28 -08002449 const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002450 MipsLabel done;
2451 if (instr->IsShl()) {
2452 __ Sllv(dst_low, lhs_low, rhs_reg);
2453 __ Nor(AT, ZERO, rhs_reg);
2454 __ Srl(TMP, lhs_low, 1);
2455 __ Srlv(TMP, TMP, AT);
2456 __ Sllv(dst_high, lhs_high, rhs_reg);
2457 __ Or(dst_high, dst_high, TMP);
2458 __ Andi(TMP, rhs_reg, kMipsBitsPerWord);
Chris Larsen3e5fecd2017-11-09 14:21:28 -08002459 if (isR6) {
2460 __ Beqzc(TMP, &done, /* is_bare */ true);
2461 __ Move(dst_high, dst_low);
2462 __ Move(dst_low, ZERO);
2463 } else {
2464 __ Movn(dst_high, dst_low, TMP);
2465 __ Movn(dst_low, ZERO, TMP);
2466 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002467 } else if (instr->IsShr()) {
2468 __ Srav(dst_high, lhs_high, rhs_reg);
2469 __ Nor(AT, ZERO, rhs_reg);
2470 __ Sll(TMP, lhs_high, 1);
2471 __ Sllv(TMP, TMP, AT);
2472 __ Srlv(dst_low, lhs_low, rhs_reg);
2473 __ Or(dst_low, dst_low, TMP);
2474 __ Andi(TMP, rhs_reg, kMipsBitsPerWord);
Chris Larsen3e5fecd2017-11-09 14:21:28 -08002475 if (isR6) {
2476 __ Beqzc(TMP, &done, /* is_bare */ true);
2477 __ Move(dst_low, dst_high);
2478 __ Sra(dst_high, dst_high, 31);
2479 } else {
2480 __ Sra(AT, dst_high, 31);
2481 __ Movn(dst_low, dst_high, TMP);
2482 __ Movn(dst_high, AT, TMP);
2483 }
Alexey Frunze92d90602015-12-18 18:16:36 -08002484 } else if (instr->IsUShr()) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002485 __ Srlv(dst_high, lhs_high, rhs_reg);
2486 __ Nor(AT, ZERO, rhs_reg);
2487 __ Sll(TMP, lhs_high, 1);
2488 __ Sllv(TMP, TMP, AT);
2489 __ Srlv(dst_low, lhs_low, rhs_reg);
2490 __ Or(dst_low, dst_low, TMP);
2491 __ Andi(TMP, rhs_reg, kMipsBitsPerWord);
Chris Larsen3e5fecd2017-11-09 14:21:28 -08002492 if (isR6) {
2493 __ Beqzc(TMP, &done, /* is_bare */ true);
2494 __ Move(dst_low, dst_high);
2495 __ Move(dst_high, ZERO);
2496 } else {
2497 __ Movn(dst_low, dst_high, TMP);
2498 __ Movn(dst_high, ZERO, TMP);
2499 }
2500 } else { // Rotate.
Alexey Frunze92d90602015-12-18 18:16:36 -08002501 __ Nor(AT, ZERO, rhs_reg);
2502 __ Srlv(TMP, lhs_low, rhs_reg);
2503 __ Sll(dst_low, lhs_high, 1);
2504 __ Sllv(dst_low, dst_low, AT);
2505 __ Or(dst_low, dst_low, TMP);
2506 __ Srlv(TMP, lhs_high, rhs_reg);
2507 __ Sll(dst_high, lhs_low, 1);
2508 __ Sllv(dst_high, dst_high, AT);
2509 __ Or(dst_high, dst_high, TMP);
2510 __ Andi(TMP, rhs_reg, kMipsBitsPerWord);
Chris Larsen3e5fecd2017-11-09 14:21:28 -08002511 if (isR6) {
2512 __ Beqzc(TMP, &done, /* is_bare */ true);
2513 __ Move(TMP, dst_high);
2514 __ Move(dst_high, dst_low);
2515 __ Move(dst_low, TMP);
2516 } else {
2517 __ Movn(AT, dst_high, TMP);
2518 __ Movn(dst_high, dst_low, TMP);
2519 __ Movn(dst_low, AT, TMP);
2520 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002521 }
2522 __ Bind(&done);
2523 }
2524 break;
2525 }
2526
2527 default:
2528 LOG(FATAL) << "Unexpected shift operation type " << type;
2529 }
2530}
2531
2532void LocationsBuilderMIPS::VisitAdd(HAdd* instruction) {
2533 HandleBinaryOp(instruction);
2534}
2535
2536void InstructionCodeGeneratorMIPS::VisitAdd(HAdd* instruction) {
2537 HandleBinaryOp(instruction);
2538}
2539
2540void LocationsBuilderMIPS::VisitAnd(HAnd* instruction) {
2541 HandleBinaryOp(instruction);
2542}
2543
2544void InstructionCodeGeneratorMIPS::VisitAnd(HAnd* instruction) {
2545 HandleBinaryOp(instruction);
2546}
2547
2548void LocationsBuilderMIPS::VisitArrayGet(HArrayGet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002549 DataType::Type type = instruction->GetType();
Alexey Frunze15958152017-02-09 19:08:30 -08002550 bool object_array_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002551 kEmitCompilerReadBarrier && (type == DataType::Type::kReference);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002552 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002553 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
2554 object_array_get_with_read_barrier
2555 ? LocationSummary::kCallOnSlowPath
2556 : LocationSummary::kNoCall);
Alexey Frunzec61c0762017-04-10 13:54:23 -07002557 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
2558 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
2559 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002560 locations->SetInAt(0, Location::RequiresRegister());
2561 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002562 if (DataType::IsFloatingPointType(type)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002563 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2564 } else {
Alexey Frunze15958152017-02-09 19:08:30 -08002565 // The output overlaps in the case of an object array get with
2566 // read barriers enabled: we do not want the move to overwrite the
2567 // array's location, as we need it to emit the read barrier.
2568 locations->SetOut(Location::RequiresRegister(),
2569 object_array_get_with_read_barrier
2570 ? Location::kOutputOverlap
2571 : Location::kNoOutputOverlap);
2572 }
2573 // We need a temporary register for the read barrier marking slow
2574 // path in CodeGeneratorMIPS::GenerateArrayLoadWithBakerReadBarrier.
2575 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Alexey Frunze4147fcc2017-06-17 19:57:27 -07002576 bool temp_needed = instruction->GetIndex()->IsConstant()
2577 ? !kBakerReadBarrierThunksEnableForFields
2578 : !kBakerReadBarrierThunksEnableForArrays;
2579 if (temp_needed) {
2580 locations->AddTemp(Location::RequiresRegister());
2581 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002582 }
2583}
2584
Tijana Jakovljevic57433862017-01-17 16:59:03 +01002585static auto GetImplicitNullChecker(HInstruction* instruction, CodeGeneratorMIPS* codegen) {
2586 auto null_checker = [codegen, instruction]() {
2587 codegen->MaybeRecordImplicitNullCheck(instruction);
Alexey Frunze2923db72016-08-20 01:55:47 -07002588 };
2589 return null_checker;
2590}
2591
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002592void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
2593 LocationSummary* locations = instruction->GetLocations();
Alexey Frunze15958152017-02-09 19:08:30 -08002594 Location obj_loc = locations->InAt(0);
2595 Register obj = obj_loc.AsRegister<Register>();
2596 Location out_loc = locations->Out();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002597 Location index = locations->InAt(1);
Vladimir Marko87f3fcb2016-04-28 15:52:11 +01002598 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Tijana Jakovljevic57433862017-01-17 16:59:03 +01002599 auto null_checker = GetImplicitNullChecker(instruction, codegen_);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002600
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002601 DataType::Type type = instruction->GetType();
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002602 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
2603 instruction->IsStringCharAt();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002604 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002605 case DataType::Type::kBool:
2606 case DataType::Type::kUint8: {
Alexey Frunze15958152017-02-09 19:08:30 -08002607 Register out = out_loc.AsRegister<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002608 if (index.IsConstant()) {
2609 size_t offset =
2610 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
Alexey Frunze2923db72016-08-20 01:55:47 -07002611 __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002612 } else {
2613 __ Addu(TMP, obj, index.AsRegister<Register>());
Alexey Frunze2923db72016-08-20 01:55:47 -07002614 __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002615 }
2616 break;
2617 }
2618
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002619 case DataType::Type::kInt8: {
Alexey Frunze15958152017-02-09 19:08:30 -08002620 Register out = out_loc.AsRegister<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002621 if (index.IsConstant()) {
2622 size_t offset =
2623 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
Alexey Frunze2923db72016-08-20 01:55:47 -07002624 __ LoadFromOffset(kLoadSignedByte, out, obj, offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002625 } else {
2626 __ Addu(TMP, obj, index.AsRegister<Register>());
Alexey Frunze2923db72016-08-20 01:55:47 -07002627 __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002628 }
2629 break;
2630 }
2631
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002632 case DataType::Type::kUint16: {
Alexey Frunze15958152017-02-09 19:08:30 -08002633 Register out = out_loc.AsRegister<Register>();
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002634 if (maybe_compressed_char_at) {
2635 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2636 __ LoadFromOffset(kLoadWord, TMP, obj, count_offset, null_checker);
2637 __ Sll(TMP, TMP, 31); // Extract compression flag into the most significant bit of TMP.
2638 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
2639 "Expecting 0=compressed, 1=uncompressed");
2640 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002641 if (index.IsConstant()) {
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002642 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
2643 if (maybe_compressed_char_at) {
2644 MipsLabel uncompressed_load, done;
2645 __ Bnez(TMP, &uncompressed_load);
2646 __ LoadFromOffset(kLoadUnsignedByte,
2647 out,
2648 obj,
2649 data_offset + (const_index << TIMES_1));
2650 __ B(&done);
2651 __ Bind(&uncompressed_load);
2652 __ LoadFromOffset(kLoadUnsignedHalfword,
2653 out,
2654 obj,
2655 data_offset + (const_index << TIMES_2));
2656 __ Bind(&done);
2657 } else {
2658 __ LoadFromOffset(kLoadUnsignedHalfword,
2659 out,
2660 obj,
2661 data_offset + (const_index << TIMES_2),
2662 null_checker);
2663 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002664 } else {
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002665 Register index_reg = index.AsRegister<Register>();
2666 if (maybe_compressed_char_at) {
2667 MipsLabel uncompressed_load, done;
2668 __ Bnez(TMP, &uncompressed_load);
2669 __ Addu(TMP, obj, index_reg);
2670 __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
2671 __ B(&done);
2672 __ Bind(&uncompressed_load);
Chris Larsencd0295d2017-03-31 15:26:54 -07002673 __ ShiftAndAdd(TMP, index_reg, obj, TIMES_2, TMP);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002674 __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
2675 __ Bind(&done);
Lena Djokica2901602017-09-21 13:50:52 +02002676 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2677 __ Addu(TMP, index_reg, obj);
2678 __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002679 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07002680 __ ShiftAndAdd(TMP, index_reg, obj, TIMES_2, TMP);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002681 __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
2682 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002683 }
2684 break;
2685 }
2686
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002687 case DataType::Type::kInt16: {
2688 Register out = out_loc.AsRegister<Register>();
2689 if (index.IsConstant()) {
2690 size_t offset =
2691 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
2692 __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset, null_checker);
Lena Djokica2901602017-09-21 13:50:52 +02002693 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2694 __ Addu(TMP, index.AsRegister<Register>(), obj);
2695 __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002696 } else {
2697 __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_2, TMP);
2698 __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
2699 }
2700 break;
2701 }
2702
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002703 case DataType::Type::kInt32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002704 DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
Alexey Frunze15958152017-02-09 19:08:30 -08002705 Register out = out_loc.AsRegister<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002706 if (index.IsConstant()) {
2707 size_t offset =
2708 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
Alexey Frunze2923db72016-08-20 01:55:47 -07002709 __ LoadFromOffset(kLoadWord, out, obj, offset, null_checker);
Lena Djokica2901602017-09-21 13:50:52 +02002710 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2711 __ Addu(TMP, index.AsRegister<Register>(), obj);
2712 __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002713 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07002714 __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
Alexey Frunze2923db72016-08-20 01:55:47 -07002715 __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002716 }
2717 break;
2718 }
2719
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002720 case DataType::Type::kReference: {
Alexey Frunze15958152017-02-09 19:08:30 -08002721 static_assert(
2722 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
2723 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
2724 // /* HeapReference<Object> */ out =
2725 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
2726 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Alexey Frunze4147fcc2017-06-17 19:57:27 -07002727 bool temp_needed = index.IsConstant()
2728 ? !kBakerReadBarrierThunksEnableForFields
2729 : !kBakerReadBarrierThunksEnableForArrays;
2730 Location temp = temp_needed ? locations->GetTemp(0) : Location::NoLocation();
Alexey Frunze15958152017-02-09 19:08:30 -08002731 // Note that a potential implicit null check is handled in this
2732 // CodeGeneratorMIPS::GenerateArrayLoadWithBakerReadBarrier call.
Alexey Frunze4147fcc2017-06-17 19:57:27 -07002733 DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
2734 if (index.IsConstant()) {
2735 // Array load with a constant index can be treated as a field load.
2736 size_t offset =
2737 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2738 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
2739 out_loc,
2740 obj,
2741 offset,
2742 temp,
2743 /* needs_null_check */ false);
2744 } else {
2745 codegen_->GenerateArrayLoadWithBakerReadBarrier(instruction,
2746 out_loc,
2747 obj,
2748 data_offset,
2749 index,
2750 temp,
2751 /* needs_null_check */ false);
2752 }
Alexey Frunze15958152017-02-09 19:08:30 -08002753 } else {
2754 Register out = out_loc.AsRegister<Register>();
2755 if (index.IsConstant()) {
2756 size_t offset =
2757 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
2758 __ LoadFromOffset(kLoadWord, out, obj, offset, null_checker);
2759 // If read barriers are enabled, emit read barriers other than
2760 // Baker's using a slow path (and also unpoison the loaded
2761 // reference, if heap poisoning is enabled).
2762 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
2763 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07002764 __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
Alexey Frunze15958152017-02-09 19:08:30 -08002765 __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
2766 // If read barriers are enabled, emit read barriers other than
2767 // Baker's using a slow path (and also unpoison the loaded
2768 // reference, if heap poisoning is enabled).
2769 codegen_->MaybeGenerateReadBarrierSlow(instruction,
2770 out_loc,
2771 out_loc,
2772 obj_loc,
2773 data_offset,
2774 index);
2775 }
2776 }
2777 break;
2778 }
2779
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002780 case DataType::Type::kInt64: {
Alexey Frunze15958152017-02-09 19:08:30 -08002781 Register out = out_loc.AsRegisterPairLow<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002782 if (index.IsConstant()) {
2783 size_t offset =
2784 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
Alexey Frunze2923db72016-08-20 01:55:47 -07002785 __ LoadFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
Lena Djokica2901602017-09-21 13:50:52 +02002786 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2787 __ Addu(TMP, index.AsRegister<Register>(), obj);
2788 __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002789 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07002790 __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_8, TMP);
Alexey Frunze2923db72016-08-20 01:55:47 -07002791 __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002792 }
2793 break;
2794 }
2795
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002796 case DataType::Type::kFloat32: {
Alexey Frunze15958152017-02-09 19:08:30 -08002797 FRegister out = out_loc.AsFpuRegister<FRegister>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002798 if (index.IsConstant()) {
2799 size_t offset =
2800 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
Alexey Frunze2923db72016-08-20 01:55:47 -07002801 __ LoadSFromOffset(out, obj, offset, null_checker);
Lena Djokica2901602017-09-21 13:50:52 +02002802 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2803 __ Addu(TMP, index.AsRegister<Register>(), obj);
2804 __ LoadSFromOffset(out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002805 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07002806 __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
Alexey Frunze2923db72016-08-20 01:55:47 -07002807 __ LoadSFromOffset(out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002808 }
2809 break;
2810 }
2811
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002812 case DataType::Type::kFloat64: {
Alexey Frunze15958152017-02-09 19:08:30 -08002813 FRegister out = out_loc.AsFpuRegister<FRegister>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002814 if (index.IsConstant()) {
2815 size_t offset =
2816 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
Alexey Frunze2923db72016-08-20 01:55:47 -07002817 __ LoadDFromOffset(out, obj, offset, null_checker);
Lena Djokica2901602017-09-21 13:50:52 +02002818 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2819 __ Addu(TMP, index.AsRegister<Register>(), obj);
2820 __ LoadDFromOffset(out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002821 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07002822 __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_8, TMP);
Alexey Frunze2923db72016-08-20 01:55:47 -07002823 __ LoadDFromOffset(out, TMP, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002824 }
2825 break;
2826 }
2827
Aart Bik66c158e2018-01-31 12:55:04 -08002828 case DataType::Type::kUint32:
2829 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002830 case DataType::Type::kVoid:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002831 LOG(FATAL) << "Unreachable type " << instruction->GetType();
2832 UNREACHABLE();
2833 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002834}
2835
2836void LocationsBuilderMIPS::VisitArrayLength(HArrayLength* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002837 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002838 locations->SetInAt(0, Location::RequiresRegister());
2839 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2840}
2841
2842void InstructionCodeGeneratorMIPS::VisitArrayLength(HArrayLength* instruction) {
2843 LocationSummary* locations = instruction->GetLocations();
Vladimir Markodce016e2016-04-28 13:10:02 +01002844 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002845 Register obj = locations->InAt(0).AsRegister<Register>();
2846 Register out = locations->Out().AsRegister<Register>();
2847 __ LoadFromOffset(kLoadWord, out, obj, offset);
2848 codegen_->MaybeRecordImplicitNullCheck(instruction);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002849 // Mask out compression flag from String's array length.
2850 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
2851 __ Srl(out, out, 1u);
2852 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002853}
2854
Alexey Frunzef58b2482016-09-02 22:14:06 -07002855Location LocationsBuilderMIPS::RegisterOrZeroConstant(HInstruction* instruction) {
2856 return (instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern())
2857 ? Location::ConstantLocation(instruction->AsConstant())
2858 : Location::RequiresRegister();
2859}
2860
2861Location LocationsBuilderMIPS::FpuRegisterOrConstantForStore(HInstruction* instruction) {
2862 // We can store 0.0 directly (from the ZERO register) without loading it into an FPU register.
2863 // We can store a non-zero float or double constant without first loading it into the FPU,
2864 // but we should only prefer this if the constant has a single use.
2865 if (instruction->IsConstant() &&
2866 (instruction->AsConstant()->IsZeroBitPattern() ||
2867 instruction->GetUses().HasExactlyOneElement())) {
2868 return Location::ConstantLocation(instruction->AsConstant());
2869 // Otherwise fall through and require an FPU register for the constant.
2870 }
2871 return Location::RequiresFpuRegister();
2872}
2873
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002874void LocationsBuilderMIPS::VisitArraySet(HArraySet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002875 DataType::Type value_type = instruction->GetComponentType();
Alexey Frunze15958152017-02-09 19:08:30 -08002876
2877 bool needs_write_barrier =
2878 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
2879 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
2880
Vladimir Markoca6fff82017-10-03 14:49:14 +01002881 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002882 instruction,
Alexey Frunze15958152017-02-09 19:08:30 -08002883 may_need_runtime_call_for_type_check ?
2884 LocationSummary::kCallOnSlowPath :
2885 LocationSummary::kNoCall);
2886
2887 locations->SetInAt(0, Location::RequiresRegister());
2888 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002889 if (DataType::IsFloatingPointType(instruction->InputAt(2)->GetType())) {
Alexey Frunze15958152017-02-09 19:08:30 -08002890 locations->SetInAt(2, FpuRegisterOrConstantForStore(instruction->InputAt(2)));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002891 } else {
Alexey Frunze15958152017-02-09 19:08:30 -08002892 locations->SetInAt(2, RegisterOrZeroConstant(instruction->InputAt(2)));
2893 }
2894 if (needs_write_barrier) {
2895 // Temporary register for the write barrier.
2896 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002897 }
2898}
2899
2900void InstructionCodeGeneratorMIPS::VisitArraySet(HArraySet* instruction) {
2901 LocationSummary* locations = instruction->GetLocations();
2902 Register obj = locations->InAt(0).AsRegister<Register>();
2903 Location index = locations->InAt(1);
Alexey Frunzef58b2482016-09-02 22:14:06 -07002904 Location value_location = locations->InAt(2);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002905 DataType::Type value_type = instruction->GetComponentType();
Alexey Frunze15958152017-02-09 19:08:30 -08002906 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002907 bool needs_write_barrier =
2908 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
Tijana Jakovljevic57433862017-01-17 16:59:03 +01002909 auto null_checker = GetImplicitNullChecker(instruction, codegen_);
Alexey Frunzef58b2482016-09-02 22:14:06 -07002910 Register base_reg = index.IsConstant() ? obj : TMP;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002911
2912 switch (value_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002913 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002914 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002915 case DataType::Type::kInt8: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002916 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002917 if (index.IsConstant()) {
Alexey Frunzef58b2482016-09-02 22:14:06 -07002918 data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002919 } else {
Alexey Frunzef58b2482016-09-02 22:14:06 -07002920 __ Addu(base_reg, obj, index.AsRegister<Register>());
2921 }
2922 if (value_location.IsConstant()) {
2923 int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
2924 __ StoreConstToOffset(kStoreByte, value, base_reg, data_offset, TMP, null_checker);
2925 } else {
2926 Register value = value_location.AsRegister<Register>();
2927 __ StoreToOffset(kStoreByte, value, base_reg, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002928 }
2929 break;
2930 }
2931
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002932 case DataType::Type::kUint16:
2933 case DataType::Type::kInt16: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002934 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002935 if (index.IsConstant()) {
Alexey Frunzef58b2482016-09-02 22:14:06 -07002936 data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2;
Lena Djokica2901602017-09-21 13:50:52 +02002937 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2938 __ Addu(base_reg, index.AsRegister<Register>(), obj);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002939 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07002940 __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_2, base_reg);
Alexey Frunzef58b2482016-09-02 22:14:06 -07002941 }
2942 if (value_location.IsConstant()) {
2943 int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
2944 __ StoreConstToOffset(kStoreHalfword, value, base_reg, data_offset, TMP, null_checker);
2945 } else {
2946 Register value = value_location.AsRegister<Register>();
2947 __ StoreToOffset(kStoreHalfword, value, base_reg, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002948 }
2949 break;
2950 }
2951
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002952 case DataType::Type::kInt32: {
Alexey Frunze15958152017-02-09 19:08:30 -08002953 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
2954 if (index.IsConstant()) {
2955 data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
Lena Djokica2901602017-09-21 13:50:52 +02002956 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
2957 __ Addu(base_reg, index.AsRegister<Register>(), obj);
Alexey Frunze15958152017-02-09 19:08:30 -08002958 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07002959 __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
Alexey Frunze15958152017-02-09 19:08:30 -08002960 }
2961 if (value_location.IsConstant()) {
2962 int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
2963 __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
2964 } else {
2965 Register value = value_location.AsRegister<Register>();
2966 __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
2967 }
2968 break;
2969 }
2970
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002971 case DataType::Type::kReference: {
Alexey Frunze15958152017-02-09 19:08:30 -08002972 if (value_location.IsConstant()) {
2973 // Just setting null.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002974 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002975 if (index.IsConstant()) {
Alexey Frunzef58b2482016-09-02 22:14:06 -07002976 data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002977 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07002978 __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02002979 }
Alexey Frunze15958152017-02-09 19:08:30 -08002980 int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
2981 DCHECK_EQ(value, 0);
2982 __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
2983 DCHECK(!needs_write_barrier);
2984 DCHECK(!may_need_runtime_call_for_type_check);
2985 break;
2986 }
2987
2988 DCHECK(needs_write_barrier);
2989 Register value = value_location.AsRegister<Register>();
2990 Register temp1 = locations->GetTemp(0).AsRegister<Register>();
2991 Register temp2 = TMP; // Doesn't need to survive slow path.
2992 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2993 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2994 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2995 MipsLabel done;
2996 SlowPathCodeMIPS* slow_path = nullptr;
2997
2998 if (may_need_runtime_call_for_type_check) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01002999 slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathMIPS(instruction);
Alexey Frunze15958152017-02-09 19:08:30 -08003000 codegen_->AddSlowPath(slow_path);
3001 if (instruction->GetValueCanBeNull()) {
3002 MipsLabel non_zero;
3003 __ Bnez(value, &non_zero);
3004 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
3005 if (index.IsConstant()) {
3006 data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
Lena Djokica2901602017-09-21 13:50:52 +02003007 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3008 __ Addu(base_reg, index.AsRegister<Register>(), obj);
Alexey Frunzec061de12017-02-14 13:27:23 -08003009 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07003010 __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
Alexey Frunzec061de12017-02-14 13:27:23 -08003011 }
Alexey Frunze15958152017-02-09 19:08:30 -08003012 __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
3013 __ B(&done);
3014 __ Bind(&non_zero);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003015 }
Alexey Frunze15958152017-02-09 19:08:30 -08003016
3017 // Note that when read barriers are enabled, the type checks
3018 // are performed without read barriers. This is fine, even in
3019 // the case where a class object is in the from-space after
3020 // the flip, as a comparison involving such a type would not
3021 // produce a false positive; it may of course produce a false
3022 // negative, in which case we would take the ArraySet slow
3023 // path.
3024
3025 // /* HeapReference<Class> */ temp1 = obj->klass_
3026 __ LoadFromOffset(kLoadWord, temp1, obj, class_offset, null_checker);
3027 __ MaybeUnpoisonHeapReference(temp1);
3028
3029 // /* HeapReference<Class> */ temp1 = temp1->component_type_
3030 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
3031 // /* HeapReference<Class> */ temp2 = value->klass_
3032 __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
3033 // If heap poisoning is enabled, no need to unpoison `temp1`
3034 // nor `temp2`, as we are comparing two poisoned references.
3035
3036 if (instruction->StaticTypeOfArrayIsObjectArray()) {
3037 MipsLabel do_put;
3038 __ Beq(temp1, temp2, &do_put);
3039 // If heap poisoning is enabled, the `temp1` reference has
3040 // not been unpoisoned yet; unpoison it now.
3041 __ MaybeUnpoisonHeapReference(temp1);
3042
3043 // /* HeapReference<Class> */ temp1 = temp1->super_class_
3044 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
3045 // If heap poisoning is enabled, no need to unpoison
3046 // `temp1`, as we are comparing against null below.
3047 __ Bnez(temp1, slow_path->GetEntryLabel());
3048 __ Bind(&do_put);
3049 } else {
3050 __ Bne(temp1, temp2, slow_path->GetEntryLabel());
3051 }
3052 }
3053
3054 Register source = value;
3055 if (kPoisonHeapReferences) {
3056 // Note that in the case where `value` is a null reference,
3057 // we do not enter this block, as a null reference does not
3058 // need poisoning.
3059 __ Move(temp1, value);
3060 __ PoisonHeapReference(temp1);
3061 source = temp1;
3062 }
3063
3064 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
3065 if (index.IsConstant()) {
3066 data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003067 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07003068 __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
Alexey Frunze15958152017-02-09 19:08:30 -08003069 }
3070 __ StoreToOffset(kStoreWord, source, base_reg, data_offset);
3071
3072 if (!may_need_runtime_call_for_type_check) {
3073 codegen_->MaybeRecordImplicitNullCheck(instruction);
3074 }
3075
3076 codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull());
3077
3078 if (done.IsLinked()) {
3079 __ Bind(&done);
3080 }
3081
3082 if (slow_path != nullptr) {
3083 __ Bind(slow_path->GetExitLabel());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003084 }
3085 break;
3086 }
3087
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003088 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003089 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003090 if (index.IsConstant()) {
Alexey Frunzef58b2482016-09-02 22:14:06 -07003091 data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
Lena Djokica2901602017-09-21 13:50:52 +02003092 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3093 __ Addu(base_reg, index.AsRegister<Register>(), obj);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003094 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07003095 __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_8, base_reg);
Alexey Frunzef58b2482016-09-02 22:14:06 -07003096 }
3097 if (value_location.IsConstant()) {
3098 int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
3099 __ StoreConstToOffset(kStoreDoubleword, value, base_reg, data_offset, TMP, null_checker);
3100 } else {
3101 Register value = value_location.AsRegisterPairLow<Register>();
3102 __ StoreToOffset(kStoreDoubleword, value, base_reg, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003103 }
3104 break;
3105 }
3106
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003107 case DataType::Type::kFloat32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003108 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003109 if (index.IsConstant()) {
Alexey Frunzef58b2482016-09-02 22:14:06 -07003110 data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
Lena Djokica2901602017-09-21 13:50:52 +02003111 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3112 __ Addu(base_reg, index.AsRegister<Register>(), obj);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003113 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07003114 __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
Alexey Frunzef58b2482016-09-02 22:14:06 -07003115 }
3116 if (value_location.IsConstant()) {
3117 int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
3118 __ StoreConstToOffset(kStoreWord, value, base_reg, data_offset, TMP, null_checker);
3119 } else {
3120 FRegister value = value_location.AsFpuRegister<FRegister>();
3121 __ StoreSToOffset(value, base_reg, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003122 }
3123 break;
3124 }
3125
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003126 case DataType::Type::kFloat64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003127 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003128 if (index.IsConstant()) {
Alexey Frunzef58b2482016-09-02 22:14:06 -07003129 data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
Lena Djokica2901602017-09-21 13:50:52 +02003130 } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
3131 __ Addu(base_reg, index.AsRegister<Register>(), obj);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003132 } else {
Chris Larsencd0295d2017-03-31 15:26:54 -07003133 __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_8, base_reg);
Alexey Frunzef58b2482016-09-02 22:14:06 -07003134 }
3135 if (value_location.IsConstant()) {
3136 int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
3137 __ StoreConstToOffset(kStoreDoubleword, value, base_reg, data_offset, TMP, null_checker);
3138 } else {
3139 FRegister value = value_location.AsFpuRegister<FRegister>();
3140 __ StoreDToOffset(value, base_reg, data_offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003141 }
3142 break;
3143 }
3144
Aart Bik66c158e2018-01-31 12:55:04 -08003145 case DataType::Type::kUint32:
3146 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003147 case DataType::Type::kVoid:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003148 LOG(FATAL) << "Unreachable type " << instruction->GetType();
3149 UNREACHABLE();
3150 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003151}
3152
Lena Djokica2901602017-09-21 13:50:52 +02003153void LocationsBuilderMIPS::VisitIntermediateArrayAddressIndex(
3154 HIntermediateArrayAddressIndex* instruction) {
3155 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003156 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Lena Djokica2901602017-09-21 13:50:52 +02003157
3158 HIntConstant* shift = instruction->GetShift()->AsIntConstant();
3159
3160 locations->SetInAt(0, Location::RequiresRegister());
3161 locations->SetInAt(1, Location::ConstantLocation(shift));
3162 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3163}
3164
3165void InstructionCodeGeneratorMIPS::VisitIntermediateArrayAddressIndex(
3166 HIntermediateArrayAddressIndex* instruction) {
3167 LocationSummary* locations = instruction->GetLocations();
3168 Register index_reg = locations->InAt(0).AsRegister<Register>();
3169 uint32_t shift = instruction->GetShift()->AsIntConstant()->GetValue();
3170 __ Sll(locations->Out().AsRegister<Register>(), index_reg, shift);
3171}
3172
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003173void LocationsBuilderMIPS::VisitBoundsCheck(HBoundsCheck* instruction) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01003174 RegisterSet caller_saves = RegisterSet::Empty();
3175 InvokeRuntimeCallingConvention calling_convention;
3176 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
3177 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
3178 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
Goran Jakovljevicdbd43032017-11-15 16:31:56 +01003179
3180 HInstruction* index = instruction->InputAt(0);
3181 HInstruction* length = instruction->InputAt(1);
3182
3183 bool const_index = false;
3184 bool const_length = false;
3185
3186 if (index->IsConstant()) {
3187 if (length->IsConstant()) {
3188 const_index = true;
3189 const_length = true;
3190 } else {
3191 int32_t index_value = index->AsIntConstant()->GetValue();
3192 if (index_value < 0 || IsInt<16>(index_value + 1)) {
3193 const_index = true;
3194 }
3195 }
3196 } else if (length->IsConstant()) {
3197 int32_t length_value = length->AsIntConstant()->GetValue();
3198 if (IsUint<15>(length_value)) {
3199 const_length = true;
3200 }
3201 }
3202
3203 locations->SetInAt(0, const_index
3204 ? Location::ConstantLocation(index->AsConstant())
3205 : Location::RequiresRegister());
3206 locations->SetInAt(1, const_length
3207 ? Location::ConstantLocation(length->AsConstant())
3208 : Location::RequiresRegister());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003209}
3210
3211void InstructionCodeGeneratorMIPS::VisitBoundsCheck(HBoundsCheck* instruction) {
3212 LocationSummary* locations = instruction->GetLocations();
Goran Jakovljevicdbd43032017-11-15 16:31:56 +01003213 Location index_loc = locations->InAt(0);
3214 Location length_loc = locations->InAt(1);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003215
Goran Jakovljevicdbd43032017-11-15 16:31:56 +01003216 if (length_loc.IsConstant()) {
3217 int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue();
3218 if (index_loc.IsConstant()) {
3219 int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
3220 if (index < 0 || index >= length) {
3221 BoundsCheckSlowPathMIPS* slow_path =
3222 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction);
3223 codegen_->AddSlowPath(slow_path);
3224 __ B(slow_path->GetEntryLabel());
3225 } else {
3226 // Nothing to be done.
3227 }
3228 return;
3229 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003230
Goran Jakovljevicdbd43032017-11-15 16:31:56 +01003231 BoundsCheckSlowPathMIPS* slow_path =
3232 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction);
3233 codegen_->AddSlowPath(slow_path);
3234 Register index = index_loc.AsRegister<Register>();
3235 if (length == 0) {
3236 __ B(slow_path->GetEntryLabel());
3237 } else if (length == 1) {
3238 __ Bnez(index, slow_path->GetEntryLabel());
3239 } else {
3240 DCHECK(IsUint<15>(length)) << length;
3241 __ Sltiu(TMP, index, length);
3242 __ Beqz(TMP, slow_path->GetEntryLabel());
3243 }
3244 } else {
3245 Register length = length_loc.AsRegister<Register>();
3246 BoundsCheckSlowPathMIPS* slow_path =
3247 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction);
3248 codegen_->AddSlowPath(slow_path);
3249 if (index_loc.IsConstant()) {
3250 int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
3251 if (index < 0) {
3252 __ B(slow_path->GetEntryLabel());
3253 } else if (index == 0) {
3254 __ Blez(length, slow_path->GetEntryLabel());
3255 } else {
3256 DCHECK(IsInt<16>(index + 1)) << index;
3257 __ Sltiu(TMP, length, index + 1);
3258 __ Bnez(TMP, slow_path->GetEntryLabel());
3259 }
3260 } else {
3261 Register index = index_loc.AsRegister<Register>();
3262 __ Bgeu(index, length, slow_path->GetEntryLabel());
3263 }
3264 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003265}
3266
Alexey Frunze15958152017-02-09 19:08:30 -08003267// Temp is used for read barrier.
3268static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
3269 if (kEmitCompilerReadBarrier &&
Alexey Frunze4147fcc2017-06-17 19:57:27 -07003270 !(kUseBakerReadBarrier && kBakerReadBarrierThunksEnableForFields) &&
Alexey Frunze15958152017-02-09 19:08:30 -08003271 (kUseBakerReadBarrier ||
3272 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
3273 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
3274 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
3275 return 1;
3276 }
3277 return 0;
3278}
3279
3280// Extra temp is used for read barrier.
3281static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
3282 return 1 + NumberOfInstanceOfTemps(type_check_kind);
3283}
3284
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003285void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) {
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003286 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
3287 bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
3288
3289 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3290 switch (type_check_kind) {
3291 case TypeCheckKind::kExactCheck:
3292 case TypeCheckKind::kAbstractClassCheck:
3293 case TypeCheckKind::kClassHierarchyCheck:
3294 case TypeCheckKind::kArrayObjectCheck:
Alexey Frunze15958152017-02-09 19:08:30 -08003295 call_kind = (throws_into_catch || kEmitCompilerReadBarrier)
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003296 ? LocationSummary::kCallOnSlowPath
3297 : LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path.
3298 break;
3299 case TypeCheckKind::kArrayCheck:
3300 case TypeCheckKind::kUnresolvedCheck:
3301 case TypeCheckKind::kInterfaceCheck:
3302 call_kind = LocationSummary::kCallOnSlowPath;
3303 break;
3304 }
3305
Vladimir Markoca6fff82017-10-03 14:49:14 +01003306 LocationSummary* locations =
3307 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003308 locations->SetInAt(0, Location::RequiresRegister());
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00003309 locations->SetInAt(1, Location::RequiresRegister());
Alexey Frunze15958152017-02-09 19:08:30 -08003310 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003311}
3312
3313void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003314 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003315 LocationSummary* locations = instruction->GetLocations();
Alexey Frunze15958152017-02-09 19:08:30 -08003316 Location obj_loc = locations->InAt(0);
3317 Register obj = obj_loc.AsRegister<Register>();
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00003318 Register cls = locations->InAt(1).AsRegister<Register>();
Alexey Frunze15958152017-02-09 19:08:30 -08003319 Location temp_loc = locations->GetTemp(0);
3320 Register temp = temp_loc.AsRegister<Register>();
3321 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
3322 DCHECK_LE(num_temps, 2u);
3323 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003324 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3325 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3326 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3327 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
3328 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
3329 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
3330 const uint32_t object_array_data_offset =
3331 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3332 MipsLabel done;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003333
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003334 // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases
3335 // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding
3336 // read barriers is done for performance and code size reasons.
3337 bool is_type_check_slow_path_fatal = false;
3338 if (!kEmitCompilerReadBarrier) {
3339 is_type_check_slow_path_fatal =
3340 (type_check_kind == TypeCheckKind::kExactCheck ||
3341 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
3342 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
3343 type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
3344 !instruction->CanThrowIntoCatchBlock();
3345 }
3346 SlowPathCodeMIPS* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01003347 new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(
3348 instruction, is_type_check_slow_path_fatal);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003349 codegen_->AddSlowPath(slow_path);
3350
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003351 // Avoid this check if we know `obj` is not null.
3352 if (instruction->MustDoNullCheck()) {
3353 __ Beqz(obj, &done);
3354 }
3355
3356 switch (type_check_kind) {
3357 case TypeCheckKind::kExactCheck:
3358 case TypeCheckKind::kArrayCheck: {
3359 // /* HeapReference<Class> */ temp = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08003360 GenerateReferenceLoadTwoRegisters(instruction,
3361 temp_loc,
3362 obj_loc,
3363 class_offset,
3364 maybe_temp2_loc,
3365 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003366 // Jump to slow path for throwing the exception or doing a
3367 // more involved array check.
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00003368 __ Bne(temp, cls, slow_path->GetEntryLabel());
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003369 break;
3370 }
3371
3372 case TypeCheckKind::kAbstractClassCheck: {
3373 // /* HeapReference<Class> */ temp = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08003374 GenerateReferenceLoadTwoRegisters(instruction,
3375 temp_loc,
3376 obj_loc,
3377 class_offset,
3378 maybe_temp2_loc,
3379 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003380 // If the class is abstract, we eagerly fetch the super class of the
3381 // object to avoid doing a comparison we know will fail.
3382 MipsLabel loop;
3383 __ Bind(&loop);
3384 // /* HeapReference<Class> */ temp = temp->super_class_
Alexey Frunze15958152017-02-09 19:08:30 -08003385 GenerateReferenceLoadOneRegister(instruction,
3386 temp_loc,
3387 super_offset,
3388 maybe_temp2_loc,
3389 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003390 // If the class reference currently in `temp` is null, jump to the slow path to throw the
3391 // exception.
3392 __ Beqz(temp, slow_path->GetEntryLabel());
3393 // Otherwise, compare the classes.
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00003394 __ Bne(temp, cls, &loop);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003395 break;
3396 }
3397
3398 case TypeCheckKind::kClassHierarchyCheck: {
3399 // /* HeapReference<Class> */ temp = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08003400 GenerateReferenceLoadTwoRegisters(instruction,
3401 temp_loc,
3402 obj_loc,
3403 class_offset,
3404 maybe_temp2_loc,
3405 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003406 // Walk over the class hierarchy to find a match.
3407 MipsLabel loop;
3408 __ Bind(&loop);
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00003409 __ Beq(temp, cls, &done);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003410 // /* HeapReference<Class> */ temp = temp->super_class_
Alexey Frunze15958152017-02-09 19:08:30 -08003411 GenerateReferenceLoadOneRegister(instruction,
3412 temp_loc,
3413 super_offset,
3414 maybe_temp2_loc,
3415 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003416 // If the class reference currently in `temp` is null, jump to the slow path to throw the
3417 // exception. Otherwise, jump to the beginning of the loop.
3418 __ Bnez(temp, &loop);
3419 __ B(slow_path->GetEntryLabel());
3420 break;
3421 }
3422
3423 case TypeCheckKind::kArrayObjectCheck: {
3424 // /* HeapReference<Class> */ temp = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08003425 GenerateReferenceLoadTwoRegisters(instruction,
3426 temp_loc,
3427 obj_loc,
3428 class_offset,
3429 maybe_temp2_loc,
3430 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003431 // Do an exact check.
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00003432 __ Beq(temp, cls, &done);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003433 // Otherwise, we need to check that the object's class is a non-primitive array.
3434 // /* HeapReference<Class> */ temp = temp->component_type_
Alexey Frunze15958152017-02-09 19:08:30 -08003435 GenerateReferenceLoadOneRegister(instruction,
3436 temp_loc,
3437 component_offset,
3438 maybe_temp2_loc,
3439 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003440 // If the component type is null, jump to the slow path to throw the exception.
3441 __ Beqz(temp, slow_path->GetEntryLabel());
3442 // Otherwise, the object is indeed an array, further check that this component
3443 // type is not a primitive type.
3444 __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
3445 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
3446 __ Bnez(temp, slow_path->GetEntryLabel());
3447 break;
3448 }
3449
3450 case TypeCheckKind::kUnresolvedCheck:
3451 // We always go into the type check slow path for the unresolved check case.
3452 // We cannot directly call the CheckCast runtime entry point
3453 // without resorting to a type checking slow path here (i.e. by
3454 // calling InvokeRuntime directly), as it would require to
3455 // assign fixed registers for the inputs of this HInstanceOf
3456 // instruction (following the runtime calling convention), which
3457 // might be cluttered by the potential first read barrier
3458 // emission at the beginning of this method.
3459 __ B(slow_path->GetEntryLabel());
3460 break;
3461
3462 case TypeCheckKind::kInterfaceCheck: {
3463 // Avoid read barriers to improve performance of the fast path. We can not get false
3464 // positives by doing this.
3465 // /* HeapReference<Class> */ temp = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08003466 GenerateReferenceLoadTwoRegisters(instruction,
3467 temp_loc,
3468 obj_loc,
3469 class_offset,
3470 maybe_temp2_loc,
3471 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003472 // /* HeapReference<Class> */ temp = temp->iftable_
Alexey Frunze15958152017-02-09 19:08:30 -08003473 GenerateReferenceLoadTwoRegisters(instruction,
3474 temp_loc,
3475 temp_loc,
3476 iftable_offset,
3477 maybe_temp2_loc,
3478 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003479 // Iftable is never null.
3480 __ Lw(TMP, temp, array_length_offset);
3481 // Loop through the iftable and check if any class matches.
3482 MipsLabel loop;
3483 __ Bind(&loop);
3484 __ Addiu(temp, temp, 2 * kHeapReferenceSize); // Possibly in delay slot on R2.
3485 __ Beqz(TMP, slow_path->GetEntryLabel());
3486 __ Lw(AT, temp, object_array_data_offset - 2 * kHeapReferenceSize);
3487 __ MaybeUnpoisonHeapReference(AT);
3488 // Go to next interface.
3489 __ Addiu(TMP, TMP, -2);
3490 // Compare the classes and continue the loop if they do not match.
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00003491 __ Bne(AT, cls, &loop);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08003492 break;
3493 }
3494 }
3495
3496 __ Bind(&done);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003497 __ Bind(slow_path->GetExitLabel());
3498}
3499
3500void LocationsBuilderMIPS::VisitClinitCheck(HClinitCheck* check) {
3501 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003502 new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003503 locations->SetInAt(0, Location::RequiresRegister());
3504 if (check->HasUses()) {
3505 locations->SetOut(Location::SameAsFirstInput());
3506 }
3507}
3508
3509void InstructionCodeGeneratorMIPS::VisitClinitCheck(HClinitCheck* check) {
3510 // We assume the class is not null.
Vladimir Marko174b2e22017-10-12 13:34:49 +01003511 SlowPathCodeMIPS* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS(
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003512 check->GetLoadClass(),
3513 check,
3514 check->GetDexPc(),
3515 true);
3516 codegen_->AddSlowPath(slow_path);
3517 GenerateClassInitializationCheck(slow_path,
3518 check->GetLocations()->InAt(0).AsRegister<Register>());
3519}
3520
3521void LocationsBuilderMIPS::VisitCompare(HCompare* compare) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003522 DataType::Type in_type = compare->InputAt(0)->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003523
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003524 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003525 new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003526
3527 switch (in_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003528 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003529 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003530 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003531 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003532 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003533 case DataType::Type::kInt32:
Alexey Frunzee7697712016-09-15 21:37:49 -07003534 locations->SetInAt(0, Location::RequiresRegister());
3535 locations->SetInAt(1, Location::RequiresRegister());
3536 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3537 break;
3538
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003539 case DataType::Type::kInt64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003540 locations->SetInAt(0, Location::RequiresRegister());
3541 locations->SetInAt(1, Location::RequiresRegister());
3542 // Output overlaps because it is written before doing the low comparison.
3543 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3544 break;
3545
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003546 case DataType::Type::kFloat32:
3547 case DataType::Type::kFloat64:
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003548 locations->SetInAt(0, Location::RequiresFpuRegister());
3549 locations->SetInAt(1, Location::RequiresFpuRegister());
3550 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003551 break;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003552
3553 default:
3554 LOG(FATAL) << "Unexpected type for compare operation " << in_type;
3555 }
3556}
3557
3558void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
3559 LocationSummary* locations = instruction->GetLocations();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003560 Register res = locations->Out().AsRegister<Register>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003561 DataType::Type in_type = instruction->InputAt(0)->GetType();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003562 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003563
3564 // 0 if: left == right
3565 // 1 if: left > right
3566 // -1 if: left < right
3567 switch (in_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003568 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003569 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003570 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003571 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003572 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003573 case DataType::Type::kInt32: {
Aart Bika19616e2016-02-01 18:57:58 -08003574 Register lhs = locations->InAt(0).AsRegister<Register>();
3575 Register rhs = locations->InAt(1).AsRegister<Register>();
3576 __ Slt(TMP, lhs, rhs);
3577 __ Slt(res, rhs, lhs);
3578 __ Subu(res, res, TMP);
3579 break;
3580 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003581 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003582 MipsLabel done;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003583 Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
3584 Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
3585 Register rhs_high = locations->InAt(1).AsRegisterPairHigh<Register>();
3586 Register rhs_low = locations->InAt(1).AsRegisterPairLow<Register>();
3587 // TODO: more efficient (direct) comparison with a constant.
3588 __ Slt(TMP, lhs_high, rhs_high);
3589 __ Slt(AT, rhs_high, lhs_high); // Inverted: is actually gt.
3590 __ Subu(res, AT, TMP); // Result -1:1:0 for [ <, >, == ].
3591 __ Bnez(res, &done); // If we compared ==, check if lower bits are also equal.
3592 __ Sltu(TMP, lhs_low, rhs_low);
3593 __ Sltu(AT, rhs_low, lhs_low); // Inverted: is actually gt.
3594 __ Subu(res, AT, TMP); // Result -1:1:0 for [ <, >, == ].
3595 __ Bind(&done);
3596 break;
3597 }
3598
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003599 case DataType::Type::kFloat32: {
Roland Levillain32ca3752016-02-17 16:49:37 +00003600 bool gt_bias = instruction->IsGtBias();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003601 FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
3602 FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
3603 MipsLabel done;
3604 if (isR6) {
3605 __ CmpEqS(FTMP, lhs, rhs);
3606 __ LoadConst32(res, 0);
3607 __ Bc1nez(FTMP, &done);
3608 if (gt_bias) {
3609 __ CmpLtS(FTMP, lhs, rhs);
3610 __ LoadConst32(res, -1);
3611 __ Bc1nez(FTMP, &done);
3612 __ LoadConst32(res, 1);
3613 } else {
3614 __ CmpLtS(FTMP, rhs, lhs);
3615 __ LoadConst32(res, 1);
3616 __ Bc1nez(FTMP, &done);
3617 __ LoadConst32(res, -1);
3618 }
3619 } else {
3620 if (gt_bias) {
3621 __ ColtS(0, lhs, rhs);
3622 __ LoadConst32(res, -1);
3623 __ Bc1t(0, &done);
3624 __ CeqS(0, lhs, rhs);
3625 __ LoadConst32(res, 1);
3626 __ Movt(res, ZERO, 0);
3627 } else {
3628 __ ColtS(0, rhs, lhs);
3629 __ LoadConst32(res, 1);
3630 __ Bc1t(0, &done);
3631 __ CeqS(0, lhs, rhs);
3632 __ LoadConst32(res, -1);
3633 __ Movt(res, ZERO, 0);
3634 }
3635 }
3636 __ Bind(&done);
3637 break;
3638 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003639 case DataType::Type::kFloat64: {
Roland Levillain32ca3752016-02-17 16:49:37 +00003640 bool gt_bias = instruction->IsGtBias();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003641 FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
3642 FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
3643 MipsLabel done;
3644 if (isR6) {
3645 __ CmpEqD(FTMP, lhs, rhs);
3646 __ LoadConst32(res, 0);
3647 __ Bc1nez(FTMP, &done);
3648 if (gt_bias) {
3649 __ CmpLtD(FTMP, lhs, rhs);
3650 __ LoadConst32(res, -1);
3651 __ Bc1nez(FTMP, &done);
3652 __ LoadConst32(res, 1);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003653 } else {
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003654 __ CmpLtD(FTMP, rhs, lhs);
3655 __ LoadConst32(res, 1);
3656 __ Bc1nez(FTMP, &done);
3657 __ LoadConst32(res, -1);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003658 }
3659 } else {
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003660 if (gt_bias) {
3661 __ ColtD(0, lhs, rhs);
3662 __ LoadConst32(res, -1);
3663 __ Bc1t(0, &done);
3664 __ CeqD(0, lhs, rhs);
3665 __ LoadConst32(res, 1);
3666 __ Movt(res, ZERO, 0);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003667 } else {
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003668 __ ColtD(0, rhs, lhs);
3669 __ LoadConst32(res, 1);
3670 __ Bc1t(0, &done);
3671 __ CeqD(0, lhs, rhs);
3672 __ LoadConst32(res, -1);
3673 __ Movt(res, ZERO, 0);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003674 }
3675 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003676 __ Bind(&done);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003677 break;
3678 }
3679
3680 default:
3681 LOG(FATAL) << "Unimplemented compare type " << in_type;
3682 }
3683}
3684
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00003685void LocationsBuilderMIPS::HandleCondition(HCondition* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003686 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003687 switch (instruction->InputAt(0)->GetType()) {
3688 default:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003689 case DataType::Type::kInt64:
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003690 locations->SetInAt(0, Location::RequiresRegister());
3691 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
3692 break;
3693
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003694 case DataType::Type::kFloat32:
3695 case DataType::Type::kFloat64:
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003696 locations->SetInAt(0, Location::RequiresFpuRegister());
3697 locations->SetInAt(1, Location::RequiresFpuRegister());
3698 break;
3699 }
David Brazdilb3e773e2016-01-26 11:28:37 +00003700 if (!instruction->IsEmittedAtUseSite()) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003701 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3702 }
3703}
3704
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00003705void InstructionCodeGeneratorMIPS::HandleCondition(HCondition* instruction) {
David Brazdilb3e773e2016-01-26 11:28:37 +00003706 if (instruction->IsEmittedAtUseSite()) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003707 return;
3708 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003709
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003710 DataType::Type type = instruction->InputAt(0)->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003711 LocationSummary* locations = instruction->GetLocations();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003712
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003713 switch (type) {
3714 default:
3715 // Integer case.
3716 GenerateIntCompare(instruction->GetCondition(), locations);
3717 return;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003718
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003719 case DataType::Type::kInt64:
Tijana Jakovljevic6d482aa2017-02-03 13:24:08 +01003720 GenerateLongCompare(instruction->GetCondition(), locations);
3721 return;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003722
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003723 case DataType::Type::kFloat32:
3724 case DataType::Type::kFloat64:
Alexey Frunze2ddb7172016-09-06 17:04:55 -07003725 GenerateFpCompare(instruction->GetCondition(), instruction->IsGtBias(), type, locations);
3726 return;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003727 }
3728}
3729
Alexey Frunze7e99e052015-11-24 19:28:01 -08003730void InstructionCodeGeneratorMIPS::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
3731 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003732 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt32);
Alexey Frunze7e99e052015-11-24 19:28:01 -08003733
3734 LocationSummary* locations = instruction->GetLocations();
3735 Location second = locations->InAt(1);
3736 DCHECK(second.IsConstant());
3737
3738 Register out = locations->Out().AsRegister<Register>();
3739 Register dividend = locations->InAt(0).AsRegister<Register>();
3740 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
3741 DCHECK(imm == 1 || imm == -1);
3742
3743 if (instruction->IsRem()) {
3744 __ Move(out, ZERO);
3745 } else {
3746 if (imm == -1) {
3747 __ Subu(out, ZERO, dividend);
3748 } else if (out != dividend) {
3749 __ Move(out, dividend);
3750 }
3751 }
3752}
3753
3754void InstructionCodeGeneratorMIPS::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
3755 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003756 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt32);
Alexey Frunze7e99e052015-11-24 19:28:01 -08003757
3758 LocationSummary* locations = instruction->GetLocations();
3759 Location second = locations->InAt(1);
3760 DCHECK(second.IsConstant());
3761
3762 Register out = locations->Out().AsRegister<Register>();
3763 Register dividend = locations->InAt(0).AsRegister<Register>();
3764 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
Nicolas Geoffray68f62892016-01-04 08:39:49 +00003765 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
Alexey Frunze7e99e052015-11-24 19:28:01 -08003766 int ctz_imm = CTZ(abs_imm);
3767
3768 if (instruction->IsDiv()) {
3769 if (ctz_imm == 1) {
3770 // Fast path for division by +/-2, which is very common.
3771 __ Srl(TMP, dividend, 31);
3772 } else {
3773 __ Sra(TMP, dividend, 31);
3774 __ Srl(TMP, TMP, 32 - ctz_imm);
3775 }
3776 __ Addu(out, dividend, TMP);
3777 __ Sra(out, out, ctz_imm);
3778 if (imm < 0) {
3779 __ Subu(out, ZERO, out);
3780 }
3781 } else {
3782 if (ctz_imm == 1) {
3783 // Fast path for modulo +/-2, which is very common.
3784 __ Sra(TMP, dividend, 31);
3785 __ Subu(out, dividend, TMP);
3786 __ Andi(out, out, 1);
3787 __ Addu(out, out, TMP);
3788 } else {
3789 __ Sra(TMP, dividend, 31);
3790 __ Srl(TMP, TMP, 32 - ctz_imm);
3791 __ Addu(out, dividend, TMP);
3792 if (IsUint<16>(abs_imm - 1)) {
3793 __ Andi(out, out, abs_imm - 1);
3794 } else {
Lena Djokica556e6b2017-12-13 12:09:42 +01003795 if (codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2()) {
3796 __ Ins(out, ZERO, ctz_imm, 32 - ctz_imm);
3797 } else {
3798 __ Sll(out, out, 32 - ctz_imm);
3799 __ Srl(out, out, 32 - ctz_imm);
3800 }
Alexey Frunze7e99e052015-11-24 19:28:01 -08003801 }
3802 __ Subu(out, out, TMP);
3803 }
3804 }
3805}
3806
3807void InstructionCodeGeneratorMIPS::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
3808 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003809 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt32);
Alexey Frunze7e99e052015-11-24 19:28:01 -08003810
3811 LocationSummary* locations = instruction->GetLocations();
3812 Location second = locations->InAt(1);
3813 DCHECK(second.IsConstant());
3814
3815 Register out = locations->Out().AsRegister<Register>();
3816 Register dividend = locations->InAt(0).AsRegister<Register>();
3817 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
3818
3819 int64_t magic;
3820 int shift;
3821 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
3822
3823 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
3824
3825 __ LoadConst32(TMP, magic);
3826 if (isR6) {
3827 __ MuhR6(TMP, dividend, TMP);
3828 } else {
3829 __ MultR2(dividend, TMP);
3830 __ Mfhi(TMP);
3831 }
3832 if (imm > 0 && magic < 0) {
3833 __ Addu(TMP, TMP, dividend);
3834 } else if (imm < 0 && magic > 0) {
3835 __ Subu(TMP, TMP, dividend);
3836 }
3837
3838 if (shift != 0) {
3839 __ Sra(TMP, TMP, shift);
3840 }
3841
3842 if (instruction->IsDiv()) {
3843 __ Sra(out, TMP, 31);
3844 __ Subu(out, TMP, out);
3845 } else {
3846 __ Sra(AT, TMP, 31);
3847 __ Subu(AT, TMP, AT);
3848 __ LoadConst32(TMP, imm);
3849 if (isR6) {
3850 __ MulR6(TMP, AT, TMP);
3851 } else {
3852 __ MulR2(TMP, AT, TMP);
3853 }
3854 __ Subu(out, dividend, TMP);
3855 }
3856}
3857
3858void InstructionCodeGeneratorMIPS::GenerateDivRemIntegral(HBinaryOperation* instruction) {
3859 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003860 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt32);
Alexey Frunze7e99e052015-11-24 19:28:01 -08003861
3862 LocationSummary* locations = instruction->GetLocations();
3863 Register out = locations->Out().AsRegister<Register>();
3864 Location second = locations->InAt(1);
3865
3866 if (second.IsConstant()) {
3867 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
3868 if (imm == 0) {
3869 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
3870 } else if (imm == 1 || imm == -1) {
3871 DivRemOneOrMinusOne(instruction);
Nicolas Geoffray68f62892016-01-04 08:39:49 +00003872 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
Alexey Frunze7e99e052015-11-24 19:28:01 -08003873 DivRemByPowerOfTwo(instruction);
3874 } else {
3875 DCHECK(imm <= -2 || imm >= 2);
3876 GenerateDivRemWithAnyConstant(instruction);
3877 }
3878 } else {
3879 Register dividend = locations->InAt(0).AsRegister<Register>();
3880 Register divisor = second.AsRegister<Register>();
3881 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
3882 if (instruction->IsDiv()) {
3883 if (isR6) {
3884 __ DivR6(out, dividend, divisor);
3885 } else {
3886 __ DivR2(out, dividend, divisor);
3887 }
3888 } else {
3889 if (isR6) {
3890 __ ModR6(out, dividend, divisor);
3891 } else {
3892 __ ModR2(out, dividend, divisor);
3893 }
3894 }
3895 }
3896}
3897
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003898void LocationsBuilderMIPS::VisitDiv(HDiv* div) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003899 DataType::Type type = div->GetResultType();
3900 LocationSummary::CallKind call_kind = (type == DataType::Type::kInt64)
Serban Constantinescu54ff4822016-07-07 18:03:19 +01003901 ? LocationSummary::kCallOnMainOnly
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003902 : LocationSummary::kNoCall;
3903
Vladimir Markoca6fff82017-10-03 14:49:14 +01003904 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(div, call_kind);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003905
3906 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003907 case DataType::Type::kInt32:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003908 locations->SetInAt(0, Location::RequiresRegister());
Alexey Frunze7e99e052015-11-24 19:28:01 -08003909 locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003910 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3911 break;
3912
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003913 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003914 InvokeRuntimeCallingConvention calling_convention;
3915 locations->SetInAt(0, Location::RegisterPairLocation(
3916 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
3917 locations->SetInAt(1, Location::RegisterPairLocation(
3918 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
3919 locations->SetOut(calling_convention.GetReturnLocation(type));
3920 break;
3921 }
3922
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003923 case DataType::Type::kFloat32:
3924 case DataType::Type::kFloat64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003925 locations->SetInAt(0, Location::RequiresFpuRegister());
3926 locations->SetInAt(1, Location::RequiresFpuRegister());
3927 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3928 break;
3929
3930 default:
3931 LOG(FATAL) << "Unexpected div type " << type;
3932 }
3933}
3934
3935void InstructionCodeGeneratorMIPS::VisitDiv(HDiv* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003936 DataType::Type type = instruction->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003937 LocationSummary* locations = instruction->GetLocations();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003938
3939 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003940 case DataType::Type::kInt32:
Alexey Frunze7e99e052015-11-24 19:28:01 -08003941 GenerateDivRemIntegral(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003942 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003943 case DataType::Type::kInt64: {
Serban Constantinescufca16662016-07-14 09:21:59 +01003944 codegen_->InvokeRuntime(kQuickLdiv, instruction, instruction->GetDexPc());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003945 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
3946 break;
3947 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003948 case DataType::Type::kFloat32:
3949 case DataType::Type::kFloat64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003950 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
3951 FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
3952 FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003953 if (type == DataType::Type::kFloat32) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003954 __ DivS(dst, lhs, rhs);
3955 } else {
3956 __ DivD(dst, lhs, rhs);
3957 }
3958 break;
3959 }
3960 default:
3961 LOG(FATAL) << "Unexpected div type " << type;
3962 }
3963}
3964
3965void LocationsBuilderMIPS::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01003966 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003967 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003968}
3969
3970void InstructionCodeGeneratorMIPS::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003971 SlowPathCodeMIPS* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01003972 new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathMIPS(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003973 codegen_->AddSlowPath(slow_path);
3974 Location value = instruction->GetLocations()->InAt(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003975 DataType::Type type = instruction->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003976
3977 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003978 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003979 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003980 case DataType::Type::kInt8:
3981 case DataType::Type::kUint16:
3982 case DataType::Type::kInt16:
3983 case DataType::Type::kInt32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003984 if (value.IsConstant()) {
3985 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
3986 __ B(slow_path->GetEntryLabel());
3987 } else {
3988 // A division by a non-null constant is valid. We don't need to perform
3989 // any check, so simply fall through.
3990 }
3991 } else {
3992 DCHECK(value.IsRegister()) << value;
3993 __ Beqz(value.AsRegister<Register>(), slow_path->GetEntryLabel());
3994 }
3995 break;
3996 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003997 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02003998 if (value.IsConstant()) {
3999 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
4000 __ B(slow_path->GetEntryLabel());
4001 } else {
4002 // A division by a non-null constant is valid. We don't need to perform
4003 // any check, so simply fall through.
4004 }
4005 } else {
4006 DCHECK(value.IsRegisterPair()) << value;
4007 __ Or(TMP, value.AsRegisterPairHigh<Register>(), value.AsRegisterPairLow<Register>());
4008 __ Beqz(TMP, slow_path->GetEntryLabel());
4009 }
4010 break;
4011 }
4012 default:
4013 LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
4014 }
4015}
4016
4017void LocationsBuilderMIPS::VisitDoubleConstant(HDoubleConstant* constant) {
4018 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004019 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02004020 locations->SetOut(Location::ConstantLocation(constant));
4021}
4022
4023void InstructionCodeGeneratorMIPS::VisitDoubleConstant(HDoubleConstant* cst ATTRIBUTE_UNUSED) {
4024 // Will be generated at use site.
4025}
4026
4027void LocationsBuilderMIPS::VisitExit(HExit* exit) {
4028 exit->SetLocations(nullptr);
4029}
4030
4031void InstructionCodeGeneratorMIPS::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
4032}
4033
4034void LocationsBuilderMIPS::VisitFloatConstant(HFloatConstant* constant) {
4035 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004036 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02004037 locations->SetOut(Location::ConstantLocation(constant));
4038}
4039
4040void InstructionCodeGeneratorMIPS::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) {
4041 // Will be generated at use site.
4042}
4043
4044void LocationsBuilderMIPS::VisitGoto(HGoto* got) {
4045 got->SetLocations(nullptr);
4046}
4047
4048void InstructionCodeGeneratorMIPS::HandleGoto(HInstruction* got, HBasicBlock* successor) {
Aart Bika8b8e9b2018-01-09 11:01:02 -08004049 if (successor->IsExitBlock()) {
4050 DCHECK(got->GetPrevious()->AlwaysThrows());
4051 return; // no code needed
4052 }
4053
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02004054 HBasicBlock* block = got->GetBlock();
4055 HInstruction* previous = got->GetPrevious();
4056 HLoopInformation* info = block->GetLoopInformation();
4057
4058 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02004059 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
4060 return;
4061 }
4062 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
4063 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
4064 }
4065 if (!codegen_->GoesToNextBlock(block, successor)) {
4066 __ B(codegen_->GetLabelOf(successor));
4067 }
4068}
4069
4070void InstructionCodeGeneratorMIPS::VisitGoto(HGoto* got) {
4071 HandleGoto(got, got->GetSuccessor());
4072}
4073
4074void LocationsBuilderMIPS::VisitTryBoundary(HTryBoundary* try_boundary) {
4075 try_boundary->SetLocations(nullptr);
4076}
4077
4078void InstructionCodeGeneratorMIPS::VisitTryBoundary(HTryBoundary* try_boundary) {
4079 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
4080 if (!successor->IsExitBlock()) {
4081 HandleGoto(try_boundary, successor);
4082 }
4083}
4084
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08004085void InstructionCodeGeneratorMIPS::GenerateIntCompare(IfCondition cond,
4086 LocationSummary* locations) {
4087 Register dst = locations->Out().AsRegister<Register>();
4088 Register lhs = locations->InAt(0).AsRegister<Register>();
4089 Location rhs_location = locations->InAt(1);
4090 Register rhs_reg = ZERO;
4091 int64_t rhs_imm = 0;
4092 bool use_imm = rhs_location.IsConstant();
4093 if (use_imm) {
4094 rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
4095 } else {
4096 rhs_reg = rhs_location.AsRegister<Register>();
4097 }
4098
4099 switch (cond) {
4100 case kCondEQ:
4101 case kCondNE:
Alexey Frunzee7697712016-09-15 21:37:49 -07004102 if (use_imm && IsInt<16>(-rhs_imm)) {
4103 if (rhs_imm == 0) {
4104 if (cond == kCondEQ) {
4105 __ Sltiu(dst, lhs, 1);
4106 } else {
4107 __ Sltu(dst, ZERO, lhs);
4108 }
4109 } else {
4110 __ Addiu(dst, lhs, -rhs_imm);
4111 if (cond == kCondEQ) {
4112 __ Sltiu(dst, dst, 1);
4113 } else {
4114 __ Sltu(dst, ZERO, dst);
4115 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08004116 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08004117 } else {
Alexey Frunzee7697712016-09-15 21:37:49 -07004118 if (use_imm && IsUint<16>(rhs_imm)) {
4119 __ Xori(dst, lhs, rhs_imm);
4120 } else {
4121 if (use_imm) {
4122 rhs_reg = TMP;
4123 __ LoadConst32(rhs_reg, rhs_imm);
4124 }
4125 __ Xor(dst, lhs, rhs_reg);
4126 }
4127 if (cond == kCondEQ) {
4128 __ Sltiu(dst, dst, 1);
4129 } else {
4130 __ Sltu(dst, ZERO, dst);
4131 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08004132 }
4133 break;
4134
4135 case kCondLT:
4136 case kCondGE:
4137 if (use_imm && IsInt<16>(rhs_imm)) {
4138 __ Slti(dst, lhs, rhs_imm);
4139 } else {
4140 if (use_imm) {
4141 rhs_reg = TMP;
4142 __ LoadConst32(rhs_reg, rhs_imm);
4143 }
4144 __ Slt(dst, lhs, rhs_reg);
4145 }
4146 if (cond == kCondGE) {
4147 // Simulate lhs >= rhs via !(lhs < rhs) since there's
4148 // only the slt instruction but no sge.
4149 __ Xori(dst, dst, 1);
4150 }
4151 break;
4152
4153 case kCondLE:
4154 case kCondGT:
4155 if (use_imm && IsInt<16>(rhs_imm + 1)) {
4156 // Simulate lhs <= rhs via lhs < rhs + 1.
4157 __ Slti(dst, lhs, rhs_imm + 1);
4158 if (cond == kCondGT) {
4159 // Simulate lhs > rhs via !(lhs <= rhs) since there's
4160 // only the slti instruction but no sgti.
4161 __ Xori(dst, dst, 1);
4162 }
4163 } else {
4164 if (use_imm) {
4165 rhs_reg = TMP;
4166 __ LoadConst32(rhs_reg, rhs_imm);
4167 }
4168 __ Slt(dst, rhs_reg, lhs);
4169 if (cond == kCondLE) {
4170 // Simulate lhs <= rhs via !(rhs < lhs) since there's
4171 // only the slt instruction but no sle.
4172 __ Xori(dst, dst, 1);
4173 }
4174 }
4175 break;
4176
4177 case kCondB:
4178 case kCondAE:
4179 if (use_imm && IsInt<16>(rhs_imm)) {
4180 // Sltiu sign-extends its 16-bit immediate operand before
4181 // the comparison and thus lets us compare directly with
4182 // unsigned values in the ranges [0, 0x7fff] and
4183 // [0xffff8000, 0xffffffff].
4184 __ Sltiu(dst, lhs, rhs_imm);
4185 } else {
4186 if (use_imm) {
4187 rhs_reg = TMP;
4188 __ LoadConst32(rhs_reg, rhs_imm);
4189 }
4190 __ Sltu(dst, lhs, rhs_reg);
4191 }
4192 if (cond == kCondAE) {
4193 // Simulate lhs >= rhs via !(lhs < rhs) since there's
4194 // only the sltu instruction but no sgeu.
4195 __ Xori(dst, dst, 1);
4196 }
4197 break;
4198
4199 case kCondBE:
4200 case kCondA:
4201 if (use_imm && (rhs_imm != -1) && IsInt<16>(rhs_imm + 1)) {
4202 // Simulate lhs <= rhs via lhs < rhs + 1.
4203 // Note that this only works if rhs + 1 does not overflow
4204 // to 0, hence the check above.
4205 // Sltiu sign-extends its 16-bit immediate operand before
4206 // the comparison and thus lets us compare directly with
4207 // unsigned values in the ranges [0, 0x7fff] and
4208 // [0xffff8000, 0xffffffff].
4209 __ Sltiu(dst, lhs, rhs_imm + 1);
4210 if (cond == kCondA) {
4211 // Simulate lhs > rhs via !(lhs <= rhs) since there's
4212 // only the sltiu instruction but no sgtiu.
4213 __ Xori(dst, dst, 1);
4214 }
4215 } else {
4216 if (use_imm) {
4217 rhs_reg = TMP;
4218 __ LoadConst32(rhs_reg, rhs_imm);
4219 }
4220 __ Sltu(dst, rhs_reg, lhs);
4221 if (cond == kCondBE) {
4222 // Simulate lhs <= rhs via !(rhs < lhs) since there's
4223 // only the sltu instruction but no sleu.
4224 __ Xori(dst, dst, 1);
4225 }
4226 }
4227 break;
4228 }
4229}
4230
Alexey Frunze674b9ee2016-09-20 14:54:15 -07004231bool InstructionCodeGeneratorMIPS::MaterializeIntCompare(IfCondition cond,
4232 LocationSummary* input_locations,
4233 Register dst) {
4234 Register lhs = input_locations->InAt(0).AsRegister<Register>();
4235 Location rhs_location = input_locations->InAt(1);
4236 Register rhs_reg = ZERO;
4237 int64_t rhs_imm = 0;
4238 bool use_imm = rhs_location.IsConstant();
4239 if (use_imm) {
4240 rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
4241 } else {
4242 rhs_reg = rhs_location.AsRegister<Register>();
4243 }
4244
4245 switch (cond) {
4246 case kCondEQ:
4247 case kCondNE:
4248 if (use_imm && IsInt<16>(-rhs_imm)) {
4249 __ Addiu(dst, lhs, -rhs_imm);
4250 } else if (use_imm && IsUint<16>(rhs_imm)) {
4251 __ Xori(dst, lhs, rhs_imm);
4252 } else {
4253 if (use_imm) {
4254 rhs_reg = TMP;
4255 __ LoadConst32(rhs_reg, rhs_imm);
4256 }
4257 __ Xor(dst, lhs, rhs_reg);
4258 }
4259 return (cond == kCondEQ);
4260
4261 case kCondLT:
4262 case kCondGE:
4263 if (use_imm && IsInt<16>(rhs_imm)) {
4264 __ Slti(dst, lhs, rhs_imm);
4265 } else {
4266 if (use_imm) {
4267 rhs_reg = TMP;
4268 __ LoadConst32(rhs_reg, rhs_imm);
4269 }
4270 __ Slt(dst, lhs, rhs_reg);
4271 }
4272 return (cond == kCondGE);
4273
4274 case kCondLE:
4275 case kCondGT:
4276 if (use_imm && IsInt<16>(rhs_imm + 1)) {
4277 // Simulate lhs <= rhs via lhs < rhs + 1.
4278 __ Slti(dst, lhs, rhs_imm + 1);
4279 return (cond == kCondGT);
4280 } else {
4281 if (use_imm) {
4282 rhs_reg = TMP;
4283 __ LoadConst32(rhs_reg, rhs_imm);
4284 }
4285 __ Slt(dst, rhs_reg, lhs);
4286 return (cond == kCondLE);
4287 }
4288
4289 case kCondB:
4290 case kCondAE:
4291 if (use_imm && IsInt<16>(rhs_imm)) {
4292 // Sltiu sign-extends its 16-bit immediate operand before
4293 // the comparison and thus lets us compare directly with
4294 // unsigned values in the ranges [0, 0x7fff] and
4295 // [0xffff8000, 0xffffffff].
4296 __ Sltiu(dst, lhs, rhs_imm);
4297 } else {
4298 if (use_imm) {
4299 rhs_reg = TMP;
4300 __ LoadConst32(rhs_reg, rhs_imm);
4301 }
4302 __ Sltu(dst, lhs, rhs_reg);
4303 }
4304 return (cond == kCondAE);
4305
4306 case kCondBE:
4307 case kCondA:
4308 if (use_imm && (rhs_imm != -1) && IsInt<16>(rhs_imm + 1)) {
4309 // Simulate lhs <= rhs via lhs < rhs + 1.
4310 // Note that this only works if rhs + 1 does not overflow
4311 // to 0, hence the check above.
4312 // Sltiu sign-extends its 16-bit immediate operand before
4313 // the comparison and thus lets us compare directly with
4314 // unsigned values in the ranges [0, 0x7fff] and
4315 // [0xffff8000, 0xffffffff].
4316 __ Sltiu(dst, lhs, rhs_imm + 1);
4317 return (cond == kCondA);
4318 } else {
4319 if (use_imm) {
4320 rhs_reg = TMP;
4321 __ LoadConst32(rhs_reg, rhs_imm);
4322 }
4323 __ Sltu(dst, rhs_reg, lhs);
4324 return (cond == kCondBE);
4325 }
4326 }
4327}
4328
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08004329void InstructionCodeGeneratorMIPS::GenerateIntCompareAndBranch(IfCondition cond,
4330 LocationSummary* locations,
4331 MipsLabel* label) {
4332 Register lhs = locations->InAt(0).AsRegister<Register>();
4333 Location rhs_location = locations->InAt(1);
4334 Register rhs_reg = ZERO;
Alexey Frunzee7697712016-09-15 21:37:49 -07004335 int64_t rhs_imm = 0;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08004336 bool use_imm = rhs_location.IsConstant();
4337 if (use_imm) {
4338 rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
4339 } else {
4340 rhs_reg = rhs_location.AsRegister<Register>();
4341 }
4342
4343 if (use_imm && rhs_imm == 0) {
4344 switch (cond) {
4345 case kCondEQ:
4346 case kCondBE: // <= 0 if zero
4347 __ Beqz(lhs, label);
4348 break;
4349 case kCondNE:
4350 case kCondA: // > 0 if non-zero
4351 __ Bnez(lhs, label);
4352 break;
4353 case kCondLT:
4354 __ Bltz(lhs, label);
4355 break;
4356 case kCondGE:
4357 __ Bgez(lhs, label);
4358 break;
4359 case kCondLE:
4360 __ Blez(lhs, label);
4361 break;
4362 case kCondGT:
4363 __ Bgtz(lhs, label);
4364 break;
4365 case kCondB: // always false
4366 break;
4367 case kCondAE: // always true
4368 __ B(label);
4369 break;
4370 }
4371 } else {
Alexey Frunzee7697712016-09-15 21:37:49 -07004372 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
4373 if (isR6 || !use_imm) {
4374 if (use_imm) {
4375 rhs_reg = TMP;
4376 __ LoadConst32(rhs_reg, rhs_imm);
4377 }
4378 switch (cond) {
4379 case kCondEQ:
4380 __ Beq(lhs, rhs_reg, label);
4381 break;
4382 case kCondNE:
4383 __ Bne(lhs, rhs_reg, label);
4384 break;
4385 case kCondLT:
4386 __ Blt(lhs, rhs_reg, label);
4387 break;
4388 case kCondGE:
4389 __ Bge(lhs, rhs_reg, label);
4390 break;
4391 case kCondLE:
4392 __ Bge(rhs_reg, lhs, label);
4393 break;
4394 case kCondGT:
4395 __ Blt(rhs_reg, lhs, label);
4396 break;
4397 case kCondB:
4398 __ Bltu(lhs, rhs_reg, label);
4399 break;
4400 case kCondAE:
4401 __ Bgeu(lhs, rhs_reg, label);
4402 break;
4403 case kCondBE:
4404 __ Bgeu(rhs_reg, lhs, label);
4405 break;
4406 case kCondA:
4407 __ Bltu(rhs_reg, lhs, label);
4408 break;
4409 }
4410 } else {
4411 // Special cases for more efficient comparison with constants on R2.
4412 switch (cond) {
4413 case kCondEQ:
4414 __ LoadConst32(TMP, rhs_imm);
4415 __ Beq(lhs, TMP, label);
4416 break;
4417 case kCondNE:
4418 __ LoadConst32(TMP, rhs_imm);
4419 __ Bne(lhs, TMP, label);
4420 break;
4421 case kCondLT:
4422 if (IsInt<16>(rhs_imm)) {
4423 __ Slti(TMP, lhs, rhs_imm);
4424 __ Bnez(TMP, label);
4425 } else {
4426 __ LoadConst32(TMP, rhs_imm);
4427 __ Blt(lhs, TMP, label);
4428 }
4429 break;
4430 case kCondGE:
4431 if (IsInt<16>(rhs_imm)) {
4432 __ Slti(TMP, lhs, rhs_imm);
4433 __ Beqz(TMP, label);
4434 } else {
4435 __ LoadConst32(TMP, rhs_imm);
4436 __ Bge(lhs, TMP, label);
4437 }
4438 break;
4439 case kCondLE:
4440 if (IsInt<16>(rhs_imm + 1)) {
4441 // Simulate lhs <= rhs via lhs < rhs + 1.
4442 __ Slti(TMP, lhs, rhs_imm + 1);
4443 __ Bnez(TMP, label);
4444 } else {
4445 __ LoadConst32(TMP, rhs_imm);
4446 __ Bge(TMP, lhs, label);
4447 }
4448 break;
4449 case kCondGT:
4450 if (IsInt<16>(rhs_imm + 1)) {
4451 // Simulate lhs > rhs via !(lhs < rhs + 1).
4452 __ Slti(TMP, lhs, rhs_imm + 1);
4453 __ Beqz(TMP, label);
4454 } else {
4455 __ LoadConst32(TMP, rhs_imm);
4456 __ Blt(TMP, lhs, label);
4457 }
4458 break;
4459 case kCondB:
4460 if (IsInt<16>(rhs_imm)) {
4461 __ Sltiu(TMP, lhs, rhs_imm);
4462 __ Bnez(TMP, label);
4463 } else {
4464 __ LoadConst32(TMP, rhs_imm);
4465 __ Bltu(lhs, TMP, label);
4466 }
4467 break;
4468 case kCondAE:
4469 if (IsInt<16>(rhs_imm)) {
4470 __ Sltiu(TMP, lhs, rhs_imm);
4471 __ Beqz(TMP, label);
4472 } else {
4473 __ LoadConst32(TMP, rhs_imm);
4474 __ Bgeu(lhs, TMP, label);
4475 }
4476 break;
4477 case kCondBE:
4478 if ((rhs_imm != -1) && IsInt<16>(rhs_imm + 1)) {
4479 // Simulate lhs <= rhs via lhs < rhs + 1.
4480 // Note that this only works if rhs + 1 does not overflow
4481 // to 0, hence the check above.
4482 __ Sltiu(TMP, lhs, rhs_imm + 1);
4483 __ Bnez(TMP, label);
4484 } else {
4485 __ LoadConst32(TMP, rhs_imm);
4486 __ Bgeu(TMP, lhs, label);
4487 }
4488 break;
4489 case kCondA:
4490 if ((rhs_imm != -1) && IsInt<16>(rhs_imm + 1)) {
4491 // Simulate lhs > rhs via !(lhs < rhs + 1).
4492 // Note that this only works if rhs + 1 does not overflow
4493 // to 0, hence the check above.
4494 __ Sltiu(TMP, lhs, rhs_imm + 1);
4495 __ Beqz(TMP, label);
4496 } else {
4497 __ LoadConst32(TMP, rhs_imm);
4498 __ Bltu(TMP, lhs, label);
4499 }
4500 break;
4501 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08004502 }
4503 }
4504}
4505
Tijana Jakovljevic6d482aa2017-02-03 13:24:08 +01004506void InstructionCodeGeneratorMIPS::GenerateLongCompare(IfCondition cond,
4507 LocationSummary* locations) {
4508 Register dst = locations->Out().AsRegister<Register>();
4509 Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
4510 Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
4511 Location rhs_location = locations->InAt(1);
4512 Register rhs_high = ZERO;
4513 Register rhs_low = ZERO;
4514 int64_t imm = 0;
4515 uint32_t imm_high = 0;
4516 uint32_t imm_low = 0;
4517 bool use_imm = rhs_location.IsConstant();
4518 if (use_imm) {
4519 imm = rhs_location.GetConstant()->AsLongConstant()->GetValue();
4520 imm_high = High32Bits(imm);
4521 imm_low = Low32Bits(imm);
4522 } else {
4523 rhs_high = rhs_location.AsRegisterPairHigh<Register>();
4524 rhs_low = rhs_location.AsRegisterPairLow<Register>();
4525 }
4526 if (use_imm && imm == 0) {
4527 switch (cond) {
4528 case kCondEQ:
4529 case kCondBE: // <= 0 if zero
4530 __ Or(dst, lhs_high, lhs_low);
4531 __ Sltiu(dst, dst, 1);
4532 break;
4533 case kCondNE:
4534 case kCondA: // > 0 if non-zero
4535 __ Or(dst, lhs_high, lhs_low);
4536 __ Sltu(dst, ZERO, dst);
4537 break;
4538 case kCondLT:
4539 __ Slt(dst, lhs_high, ZERO);
4540 break;
4541 case kCondGE:
4542 __ Slt(dst, lhs_high, ZERO);
4543 __ Xori(dst, dst, 1);
4544 break;
4545 case kCondLE:
4546 __ Or(TMP, lhs_high, lhs_low);
4547 __ Sra(AT, lhs_high, 31);
4548 __ Sltu(dst, AT, TMP);
4549 __ Xori(dst, dst, 1);
4550 break;
4551 case kCondGT:
4552 __ Or(TMP, lhs_high, lhs_low);
4553 __ Sra(AT, lhs_high, 31);
4554 __ Sltu(dst, AT, TMP);
4555 break;
4556 case kCondB: // always false
4557 __ Andi(dst, dst, 0);
4558 break;
4559 case kCondAE: // always true
4560 __ Ori(dst, ZERO, 1);
4561 break;
4562 }
4563 } else if (use_imm) {
4564 // TODO: more efficient comparison with constants without loading them into TMP/AT.
4565 switch (cond) {
4566 case kCondEQ:
4567 __ LoadConst32(TMP, imm_high);
4568 __ Xor(TMP, TMP, lhs_high);
4569 __ LoadConst32(AT, imm_low);
4570 __ Xor(AT, AT, lhs_low);
4571 __ Or(dst, TMP, AT);
4572 __ Sltiu(dst, dst, 1);
4573 break;
4574 case kCondNE:
4575 __ LoadConst32(TMP, imm_high);
4576 __ Xor(TMP, TMP, lhs_high);
4577 __ LoadConst32(AT, imm_low);
4578 __ Xor(AT, AT, lhs_low);
4579 __ Or(dst, TMP, AT);
4580 __ Sltu(dst, ZERO, dst);
4581 break;
4582 case kCondLT:
4583 case kCondGE:
4584 if (dst == lhs_low) {
4585 __ LoadConst32(TMP, imm_low);
4586 __ Sltu(dst, lhs_low, TMP);
4587 }
4588 __ LoadConst32(TMP, imm_high);
4589 __ Slt(AT, lhs_high, TMP);
4590 __ Slt(TMP, TMP, lhs_high);
4591 if (dst != lhs_low) {
4592 __ LoadConst32(dst, imm_low);
4593 __ Sltu(dst, lhs_low, dst);
4594 }
4595 __ Slt(dst, TMP, dst);
4596 __ Or(dst, dst, AT);
4597 if (cond == kCondGE) {
4598 __ Xori(dst, dst, 1);
4599 }
4600 break;
4601 case kCondGT:
4602 case kCondLE:
4603 if (dst == lhs_low) {
4604 __ LoadConst32(TMP, imm_low);
4605 __ Sltu(dst, TMP, lhs_low);
4606 }
4607 __ LoadConst32(TMP, imm_high);
4608 __ Slt(AT, TMP, lhs_high);
4609 __ Slt(TMP, lhs_high, TMP);
4610 if (dst != lhs_low) {
4611 __ LoadConst32(dst, imm_low);
4612 __ Sltu(dst, dst, lhs_low);
4613 }
4614 __ Slt(dst, TMP, dst);
4615 __ Or(dst, dst, AT);
4616 if (cond == kCondLE) {
4617 __ Xori(dst, dst, 1);
4618 }
4619 break;
4620 case kCondB:
4621 case kCondAE:
4622 if (dst == lhs_low) {
4623 __ LoadConst32(TMP, imm_low);
4624 __ Sltu(dst, lhs_low, TMP);
4625 }
4626 __ LoadConst32(TMP, imm_high);
4627 __ Sltu(AT, lhs_high, TMP);
4628 __ Sltu(TMP, TMP, lhs_high);
4629 if (dst != lhs_low) {
4630 __ LoadConst32(dst, imm_low);
4631 __ Sltu(dst, lhs_low, dst);
4632 }
4633 __ Slt(dst, TMP, dst);
4634 __ Or(dst, dst, AT);
4635 if (cond == kCondAE) {
4636 __ Xori(dst, dst, 1);
4637 }
4638 break;
4639 case kCondA:
4640 case kCondBE:
4641 if (dst == lhs_low) {
4642 __ LoadConst32(TMP, imm_low);
4643 __ Sltu(dst, TMP, lhs_low);
4644 }
4645 __ LoadConst32(TMP, imm_high);
4646 __ Sltu(AT, TMP, lhs_high);
4647 __ Sltu(TMP, lhs_high, TMP);
4648 if (dst != lhs_low) {
4649 __ LoadConst32(dst, imm_low);
4650 __ Sltu(dst, dst, lhs_low);
4651 }
4652 __ Slt(dst, TMP, dst);
4653 __ Or(dst, dst, AT);
4654 if (cond == kCondBE) {
4655 __ Xori(dst, dst, 1);
4656 }
4657 break;
4658 }
4659 } else {
4660 switch (cond) {
4661 case kCondEQ:
4662 __ Xor(TMP, lhs_high, rhs_high);
4663 __ Xor(AT, lhs_low, rhs_low);
4664 __ Or(dst, TMP, AT);
4665 __ Sltiu(dst, dst, 1);
4666 break;
4667 case kCondNE:
4668 __ Xor(TMP, lhs_high, rhs_high);
4669 __ Xor(AT, lhs_low, rhs_low);
4670 __ Or(dst, TMP, AT);
4671 __ Sltu(dst, ZERO, dst);
4672 break;
4673 case kCondLT:
4674 case kCondGE:
4675 __ Slt(TMP, rhs_high, lhs_high);
4676 __ Sltu(AT, lhs_low, rhs_low);
4677 __ Slt(TMP, TMP, AT);
4678 __ Slt(AT, lhs_high, rhs_high);
4679 __ Or(dst, AT, TMP);
4680 if (cond == kCondGE) {
4681 __ Xori(dst, dst, 1);
4682 }
4683 break;
4684 case kCondGT:
4685 case kCondLE:
4686 __ Slt(TMP, lhs_high, rhs_high);
4687 __ Sltu(AT, rhs_low, lhs_low);
4688 __ Slt(TMP, TMP, AT);
4689 __ Slt(AT, rhs_high, lhs_high);
4690 __ Or(dst, AT, TMP);
4691 if (cond == kCondLE) {
4692 __ Xori(dst, dst, 1);
4693 }
4694 break;
4695 case kCondB:
4696 case kCondAE:
4697 __ Sltu(TMP, rhs_high, lhs_high);
4698 __ Sltu(AT, lhs_low, rhs_low);
4699 __ Slt(TMP, TMP, AT);
4700 __ Sltu(AT, lhs_high, rhs_high);
4701 __ Or(dst, AT, TMP);
4702 if (cond == kCondAE) {
4703 __ Xori(dst, dst, 1);
4704 }
4705 break;
4706 case kCondA:
4707 case kCondBE:
4708 __ Sltu(TMP, lhs_high, rhs_high);
4709 __ Sltu(AT, rhs_low, lhs_low);
4710 __ Slt(TMP, TMP, AT);
4711 __ Sltu(AT, rhs_high, lhs_high);
4712 __ Or(dst, AT, TMP);
4713 if (cond == kCondBE) {
4714 __ Xori(dst, dst, 1);
4715 }
4716 break;
4717 }
4718 }
4719}
4720
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08004721void InstructionCodeGeneratorMIPS::GenerateLongCompareAndBranch(IfCondition cond,
4722 LocationSummary* locations,
4723 MipsLabel* label) {
4724 Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
4725 Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
4726 Location rhs_location = locations->InAt(1);
4727 Register rhs_high = ZERO;
4728 Register rhs_low = ZERO;
4729 int64_t imm = 0;
4730 uint32_t imm_high = 0;
4731 uint32_t imm_low = 0;
4732 bool use_imm = rhs_location.IsConstant();
4733 if (use_imm) {
4734 imm = rhs_location.GetConstant()->AsLongConstant()->GetValue();
4735 imm_high = High32Bits(imm);
4736 imm_low = Low32Bits(imm);
4737 } else {
4738 rhs_high = rhs_location.AsRegisterPairHigh<Register>();
4739 rhs_low = rhs_location.AsRegisterPairLow<Register>();
4740 }
4741
4742 if (use_imm && imm == 0) {
4743 switch (cond) {
4744 case kCondEQ:
4745 case kCondBE: // <= 0 if zero
4746 __ Or(TMP, lhs_high, lhs_low);
4747 __ Beqz(TMP, label);
4748 break;
4749 case kCondNE:
4750 case kCondA: // > 0 if non-zero
4751 __ Or(TMP, lhs_high, lhs_low);
4752 __ Bnez(TMP, label);
4753 break;
4754 case kCondLT:
4755 __ Bltz(lhs_high, label);
4756 break;
4757 case kCondGE:
4758 __ Bgez(lhs_high, label);
4759 break;
4760 case kCondLE:
4761 __ Or(TMP, lhs_high, lhs_low);
4762 __ Sra(AT, lhs_high, 31);
4763 __ Bgeu(AT, TMP, label);
4764 break;
4765 case kCondGT:
4766 __ Or(TMP, lhs_high, lhs_low);
4767 __ Sra(AT, lhs_high, 31);
4768 __ Bltu(AT, TMP, label);
4769 break;
4770 case kCondB: // always false
4771 break;
4772 case kCondAE: // always true
4773 __ B(label);
4774 break;
4775 }
4776 } else if (use_imm) {
4777 // TODO: more efficient comparison with constants without loading them into TMP/AT.
4778 switch (cond) {
4779 case kCondEQ:
4780 __ LoadConst32(TMP, imm_high);
4781 __ Xor(TMP, TMP, lhs_high);
4782 __ LoadConst32(AT, imm_low);
4783 __ Xor(AT, AT, lhs_low);
4784 __ Or(TMP, TMP, AT);
4785 __ Beqz(TMP, label);
4786 break;
4787 case kCondNE:
4788 __ LoadConst32(TMP, imm_high);
4789 __ Xor(TMP, TMP, lhs_high);
4790 __ LoadConst32(AT, imm_low);
4791 __ Xor(AT, AT, lhs_low);
4792 __ Or(TMP, TMP, AT);
4793 __ Bnez(TMP, label);
4794 break;
4795 case kCondLT:
4796 __ LoadConst32(TMP, imm_high);
4797 __ Blt(lhs_high, TMP, label);
4798 __ Slt(TMP, TMP, lhs_high);
4799 __ LoadConst32(AT, imm_low);
4800 __ Sltu(AT, lhs_low, AT);
4801 __ Blt(TMP, AT, label);
4802 break;
4803 case kCondGE:
4804 __ LoadConst32(TMP, imm_high);
4805 __ Blt(TMP, lhs_high, label);
4806 __ Slt(TMP, lhs_high, TMP);
4807 __ LoadConst32(AT, imm_low);
4808 __ Sltu(AT, lhs_low, AT);
4809 __ Or(TMP, TMP, AT);
4810 __ Beqz(TMP, label);
4811 break;
4812 case kCondLE:
4813 __ LoadConst32(TMP, imm_high);
4814 __ Blt(lhs_high, TMP, label);
4815 __ Slt(TMP, TMP, lhs_high);
4816 __ LoadConst32(AT, imm_low);
4817 __ Sltu(AT, AT, lhs_low);
4818 __ Or(TMP, TMP, AT);
4819 __ Beqz(TMP, label);
4820 break;
4821 case kCondGT:
4822 __ LoadConst32(TMP, imm_high);
4823 __ Blt(TMP, lhs_high, label);
4824 __ Slt(TMP, lhs_high, TMP);
4825 __ LoadConst32(AT, imm_low);
4826 __ Sltu(AT, AT, lhs_low);
4827 __ Blt(TMP, AT, label);
4828 break;
4829 case kCondB:
4830 __ LoadConst32(TMP, imm_high);
4831 __ Bltu(lhs_high, TMP, label);
4832 __ Sltu(TMP, TMP, lhs_high);
4833 __ LoadConst32(AT, imm_low);
4834 __ Sltu(AT, lhs_low, AT);
4835 __ Blt(TMP, AT, label);
4836 break;
4837 case kCondAE:
4838 __ LoadConst32(TMP, imm_high);
4839 __ Bltu(TMP, lhs_high, label);
4840 __ Sltu(TMP, lhs_high, TMP);
4841 __ LoadConst32(AT, imm_low);
4842 __ Sltu(AT, lhs_low, AT);
4843 __ Or(TMP, TMP, AT);
4844 __ Beqz(TMP, label);
4845 break;
4846 case kCondBE:
4847 __ LoadConst32(TMP, imm_high);
4848 __ Bltu(lhs_high, TMP, label);
4849 __ Sltu(TMP, TMP, lhs_high);
4850 __ LoadConst32(AT, imm_low);
4851 __ Sltu(AT, AT, lhs_low);
4852 __ Or(TMP, TMP, AT);
4853 __ Beqz(TMP, label);
4854 break;
4855 case kCondA:
4856 __ LoadConst32(TMP, imm_high);
4857 __ Bltu(TMP, lhs_high, label);
4858 __ Sltu(TMP, lhs_high, TMP);
4859 __ LoadConst32(AT, imm_low);
4860 __ Sltu(AT, AT, lhs_low);
4861 __ Blt(TMP, AT, label);
4862 break;
4863 }
4864 } else {
4865 switch (cond) {
4866 case kCondEQ:
4867 __ Xor(TMP, lhs_high, rhs_high);
4868 __ Xor(AT, lhs_low, rhs_low);
4869 __ Or(TMP, TMP, AT);
4870 __ Beqz(TMP, label);
4871 break;
4872 case kCondNE:
4873 __ Xor(TMP, lhs_high, rhs_high);
4874 __ Xor(AT, lhs_low, rhs_low);
4875 __ Or(TMP, TMP, AT);
4876 __ Bnez(TMP, label);
4877 break;
4878 case kCondLT:
4879 __ Blt(lhs_high, rhs_high, label);
4880 __ Slt(TMP, rhs_high, lhs_high);
4881 __ Sltu(AT, lhs_low, rhs_low);
4882 __ Blt(TMP, AT, label);
4883 break;
4884 case kCondGE:
4885 __ Blt(rhs_high, lhs_high, label);
4886 __ Slt(TMP, lhs_high, rhs_high);
4887 __ Sltu(AT, lhs_low, rhs_low);
4888 __ Or(TMP, TMP, AT);
4889 __ Beqz(TMP, label);
4890 break;
4891 case kCondLE:
4892 __ Blt(lhs_high, rhs_high, label);
4893 __ Slt(TMP, rhs_high, lhs_high);
4894 __ Sltu(AT, rhs_low, lhs_low);
4895 __ Or(TMP, TMP, AT);
4896 __ Beqz(TMP, label);
4897 break;
4898 case kCondGT:
4899 __ Blt(rhs_high, lhs_high, label);
4900 __ Slt(TMP, lhs_high, rhs_high);
4901 __ Sltu(AT, rhs_low, lhs_low);
4902 __ Blt(TMP, AT, label);
4903 break;
4904 case kCondB:
4905 __ Bltu(lhs_high, rhs_high, label);
4906 __ Sltu(TMP, rhs_high, lhs_high);
4907 __ Sltu(AT, lhs_low, rhs_low);
4908 __ Blt(TMP, AT, label);
4909 break;
4910 case kCondAE:
4911 __ Bltu(rhs_high, lhs_high, label);
4912 __ Sltu(TMP, lhs_high, rhs_high);
4913 __ Sltu(AT, lhs_low, rhs_low);
4914 __ Or(TMP, TMP, AT);
4915 __ Beqz(TMP, label);
4916 break;
4917 case kCondBE:
4918 __ Bltu(lhs_high, rhs_high, label);
4919 __ Sltu(TMP, rhs_high, lhs_high);
4920 __ Sltu(AT, rhs_low, lhs_low);
4921 __ Or(TMP, TMP, AT);
4922 __ Beqz(TMP, label);
4923 break;
4924 case kCondA:
4925 __ Bltu(rhs_high, lhs_high, label);
4926 __ Sltu(TMP, lhs_high, rhs_high);
4927 __ Sltu(AT, rhs_low, lhs_low);
4928 __ Blt(TMP, AT, label);
4929 break;
4930 }
4931 }
4932}
4933
Alexey Frunze2ddb7172016-09-06 17:04:55 -07004934void InstructionCodeGeneratorMIPS::GenerateFpCompare(IfCondition cond,
4935 bool gt_bias,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004936 DataType::Type type,
Alexey Frunze2ddb7172016-09-06 17:04:55 -07004937 LocationSummary* locations) {
4938 Register dst = locations->Out().AsRegister<Register>();
4939 FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
4940 FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
4941 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004942 if (type == DataType::Type::kFloat32) {
Alexey Frunze2ddb7172016-09-06 17:04:55 -07004943 if (isR6) {
4944 switch (cond) {
4945 case kCondEQ:
4946 __ CmpEqS(FTMP, lhs, rhs);
4947 __ Mfc1(dst, FTMP);
4948 __ Andi(dst, dst, 1);
4949 break;
4950 case kCondNE:
4951 __ CmpEqS(FTMP, lhs, rhs);
4952 __ Mfc1(dst, FTMP);
4953 __ Addiu(dst, dst, 1);
4954 break;
4955 case kCondLT:
4956 if (gt_bias) {
4957 __ CmpLtS(FTMP, lhs, rhs);
4958 } else {
4959 __ CmpUltS(FTMP, lhs, rhs);
4960 }
4961 __ Mfc1(dst, FTMP);
4962 __ Andi(dst, dst, 1);
4963 break;
4964 case kCondLE:
4965 if (gt_bias) {
4966 __ CmpLeS(FTMP, lhs, rhs);
4967 } else {
4968 __ CmpUleS(FTMP, lhs, rhs);
4969 }
4970 __ Mfc1(dst, FTMP);
4971 __ Andi(dst, dst, 1);
4972 break;
4973 case kCondGT:
4974 if (gt_bias) {
4975 __ CmpUltS(FTMP, rhs, lhs);
4976 } else {
4977 __ CmpLtS(FTMP, rhs, lhs);
4978 }
4979 __ Mfc1(dst, FTMP);
4980 __ Andi(dst, dst, 1);
4981 break;
4982 case kCondGE:
4983 if (gt_bias) {
4984 __ CmpUleS(FTMP, rhs, lhs);
4985 } else {
4986 __ CmpLeS(FTMP, rhs, lhs);
4987 }
4988 __ Mfc1(dst, FTMP);
4989 __ Andi(dst, dst, 1);
4990 break;
4991 default:
4992 LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
4993 UNREACHABLE();
4994 }
4995 } else {
4996 switch (cond) {
4997 case kCondEQ:
4998 __ CeqS(0, lhs, rhs);
4999 __ LoadConst32(dst, 1);
5000 __ Movf(dst, ZERO, 0);
5001 break;
5002 case kCondNE:
5003 __ CeqS(0, lhs, rhs);
5004 __ LoadConst32(dst, 1);
5005 __ Movt(dst, ZERO, 0);
5006 break;
5007 case kCondLT:
5008 if (gt_bias) {
5009 __ ColtS(0, lhs, rhs);
5010 } else {
5011 __ CultS(0, lhs, rhs);
5012 }
5013 __ LoadConst32(dst, 1);
5014 __ Movf(dst, ZERO, 0);
5015 break;
5016 case kCondLE:
5017 if (gt_bias) {
5018 __ ColeS(0, lhs, rhs);
5019 } else {
5020 __ CuleS(0, lhs, rhs);
5021 }
5022 __ LoadConst32(dst, 1);
5023 __ Movf(dst, ZERO, 0);
5024 break;
5025 case kCondGT:
5026 if (gt_bias) {
5027 __ CultS(0, rhs, lhs);
5028 } else {
5029 __ ColtS(0, rhs, lhs);
5030 }
5031 __ LoadConst32(dst, 1);
5032 __ Movf(dst, ZERO, 0);
5033 break;
5034 case kCondGE:
5035 if (gt_bias) {
5036 __ CuleS(0, rhs, lhs);
5037 } else {
5038 __ ColeS(0, rhs, lhs);
5039 }
5040 __ LoadConst32(dst, 1);
5041 __ Movf(dst, ZERO, 0);
5042 break;
5043 default:
5044 LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
5045 UNREACHABLE();
5046 }
5047 }
5048 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005049 DCHECK_EQ(type, DataType::Type::kFloat64);
Alexey Frunze2ddb7172016-09-06 17:04:55 -07005050 if (isR6) {
5051 switch (cond) {
5052 case kCondEQ:
5053 __ CmpEqD(FTMP, lhs, rhs);
5054 __ Mfc1(dst, FTMP);
5055 __ Andi(dst, dst, 1);
5056 break;
5057 case kCondNE:
5058 __ CmpEqD(FTMP, lhs, rhs);
5059 __ Mfc1(dst, FTMP);
5060 __ Addiu(dst, dst, 1);
5061 break;
5062 case kCondLT:
5063 if (gt_bias) {
5064 __ CmpLtD(FTMP, lhs, rhs);
5065 } else {
5066 __ CmpUltD(FTMP, lhs, rhs);
5067 }
5068 __ Mfc1(dst, FTMP);
5069 __ Andi(dst, dst, 1);
5070 break;
5071 case kCondLE:
5072 if (gt_bias) {
5073 __ CmpLeD(FTMP, lhs, rhs);
5074 } else {
5075 __ CmpUleD(FTMP, lhs, rhs);
5076 }
5077 __ Mfc1(dst, FTMP);
5078 __ Andi(dst, dst, 1);
5079 break;
5080 case kCondGT:
5081 if (gt_bias) {
5082 __ CmpUltD(FTMP, rhs, lhs);
5083 } else {
5084 __ CmpLtD(FTMP, rhs, lhs);
5085 }
5086 __ Mfc1(dst, FTMP);
5087 __ Andi(dst, dst, 1);
5088 break;
5089 case kCondGE:
5090 if (gt_bias) {
5091 __ CmpUleD(FTMP, rhs, lhs);
5092 } else {
5093 __ CmpLeD(FTMP, rhs, lhs);
5094 }
5095 __ Mfc1(dst, FTMP);
5096 __ Andi(dst, dst, 1);
5097 break;
5098 default:
5099 LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
5100 UNREACHABLE();
5101 }
5102 } else {
5103 switch (cond) {
5104 case kCondEQ:
5105 __ CeqD(0, lhs, rhs);
5106 __ LoadConst32(dst, 1);
5107 __ Movf(dst, ZERO, 0);
5108 break;
5109 case kCondNE:
5110 __ CeqD(0, lhs, rhs);
5111 __ LoadConst32(dst, 1);
5112 __ Movt(dst, ZERO, 0);
5113 break;
5114 case kCondLT:
5115 if (gt_bias) {
5116 __ ColtD(0, lhs, rhs);
5117 } else {
5118 __ CultD(0, lhs, rhs);
5119 }
5120 __ LoadConst32(dst, 1);
5121 __ Movf(dst, ZERO, 0);
5122 break;
5123 case kCondLE:
5124 if (gt_bias) {
5125 __ ColeD(0, lhs, rhs);
5126 } else {
5127 __ CuleD(0, lhs, rhs);
5128 }
5129 __ LoadConst32(dst, 1);
5130 __ Movf(dst, ZERO, 0);
5131 break;
5132 case kCondGT:
5133 if (gt_bias) {
5134 __ CultD(0, rhs, lhs);
5135 } else {
5136 __ ColtD(0, rhs, lhs);
5137 }
5138 __ LoadConst32(dst, 1);
5139 __ Movf(dst, ZERO, 0);
5140 break;
5141 case kCondGE:
5142 if (gt_bias) {
5143 __ CuleD(0, rhs, lhs);
5144 } else {
5145 __ ColeD(0, rhs, lhs);
5146 }
5147 __ LoadConst32(dst, 1);
5148 __ Movf(dst, ZERO, 0);
5149 break;
5150 default:
5151 LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
5152 UNREACHABLE();
5153 }
5154 }
5155 }
5156}
5157
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005158bool InstructionCodeGeneratorMIPS::MaterializeFpCompareR2(IfCondition cond,
5159 bool gt_bias,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005160 DataType::Type type,
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005161 LocationSummary* input_locations,
5162 int cc) {
5163 FRegister lhs = input_locations->InAt(0).AsFpuRegister<FRegister>();
5164 FRegister rhs = input_locations->InAt(1).AsFpuRegister<FRegister>();
5165 CHECK(!codegen_->GetInstructionSetFeatures().IsR6());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005166 if (type == DataType::Type::kFloat32) {
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005167 switch (cond) {
5168 case kCondEQ:
5169 __ CeqS(cc, lhs, rhs);
5170 return false;
5171 case kCondNE:
5172 __ CeqS(cc, lhs, rhs);
5173 return true;
5174 case kCondLT:
5175 if (gt_bias) {
5176 __ ColtS(cc, lhs, rhs);
5177 } else {
5178 __ CultS(cc, lhs, rhs);
5179 }
5180 return false;
5181 case kCondLE:
5182 if (gt_bias) {
5183 __ ColeS(cc, lhs, rhs);
5184 } else {
5185 __ CuleS(cc, lhs, rhs);
5186 }
5187 return false;
5188 case kCondGT:
5189 if (gt_bias) {
5190 __ CultS(cc, rhs, lhs);
5191 } else {
5192 __ ColtS(cc, rhs, lhs);
5193 }
5194 return false;
5195 case kCondGE:
5196 if (gt_bias) {
5197 __ CuleS(cc, rhs, lhs);
5198 } else {
5199 __ ColeS(cc, rhs, lhs);
5200 }
5201 return false;
5202 default:
5203 LOG(FATAL) << "Unexpected non-floating-point condition";
5204 UNREACHABLE();
5205 }
5206 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005207 DCHECK_EQ(type, DataType::Type::kFloat64);
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005208 switch (cond) {
5209 case kCondEQ:
5210 __ CeqD(cc, lhs, rhs);
5211 return false;
5212 case kCondNE:
5213 __ CeqD(cc, lhs, rhs);
5214 return true;
5215 case kCondLT:
5216 if (gt_bias) {
5217 __ ColtD(cc, lhs, rhs);
5218 } else {
5219 __ CultD(cc, lhs, rhs);
5220 }
5221 return false;
5222 case kCondLE:
5223 if (gt_bias) {
5224 __ ColeD(cc, lhs, rhs);
5225 } else {
5226 __ CuleD(cc, lhs, rhs);
5227 }
5228 return false;
5229 case kCondGT:
5230 if (gt_bias) {
5231 __ CultD(cc, rhs, lhs);
5232 } else {
5233 __ ColtD(cc, rhs, lhs);
5234 }
5235 return false;
5236 case kCondGE:
5237 if (gt_bias) {
5238 __ CuleD(cc, rhs, lhs);
5239 } else {
5240 __ ColeD(cc, rhs, lhs);
5241 }
5242 return false;
5243 default:
5244 LOG(FATAL) << "Unexpected non-floating-point condition";
5245 UNREACHABLE();
5246 }
5247 }
5248}
5249
5250bool InstructionCodeGeneratorMIPS::MaterializeFpCompareR6(IfCondition cond,
5251 bool gt_bias,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005252 DataType::Type type,
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005253 LocationSummary* input_locations,
5254 FRegister dst) {
5255 FRegister lhs = input_locations->InAt(0).AsFpuRegister<FRegister>();
5256 FRegister rhs = input_locations->InAt(1).AsFpuRegister<FRegister>();
5257 CHECK(codegen_->GetInstructionSetFeatures().IsR6());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005258 if (type == DataType::Type::kFloat32) {
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005259 switch (cond) {
5260 case kCondEQ:
5261 __ CmpEqS(dst, lhs, rhs);
5262 return false;
5263 case kCondNE:
5264 __ CmpEqS(dst, lhs, rhs);
5265 return true;
5266 case kCondLT:
5267 if (gt_bias) {
5268 __ CmpLtS(dst, lhs, rhs);
5269 } else {
5270 __ CmpUltS(dst, lhs, rhs);
5271 }
5272 return false;
5273 case kCondLE:
5274 if (gt_bias) {
5275 __ CmpLeS(dst, lhs, rhs);
5276 } else {
5277 __ CmpUleS(dst, lhs, rhs);
5278 }
5279 return false;
5280 case kCondGT:
5281 if (gt_bias) {
5282 __ CmpUltS(dst, rhs, lhs);
5283 } else {
5284 __ CmpLtS(dst, rhs, lhs);
5285 }
5286 return false;
5287 case kCondGE:
5288 if (gt_bias) {
5289 __ CmpUleS(dst, rhs, lhs);
5290 } else {
5291 __ CmpLeS(dst, rhs, lhs);
5292 }
5293 return false;
5294 default:
5295 LOG(FATAL) << "Unexpected non-floating-point condition";
5296 UNREACHABLE();
5297 }
5298 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005299 DCHECK_EQ(type, DataType::Type::kFloat64);
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005300 switch (cond) {
5301 case kCondEQ:
5302 __ CmpEqD(dst, lhs, rhs);
5303 return false;
5304 case kCondNE:
5305 __ CmpEqD(dst, lhs, rhs);
5306 return true;
5307 case kCondLT:
5308 if (gt_bias) {
5309 __ CmpLtD(dst, lhs, rhs);
5310 } else {
5311 __ CmpUltD(dst, lhs, rhs);
5312 }
5313 return false;
5314 case kCondLE:
5315 if (gt_bias) {
5316 __ CmpLeD(dst, lhs, rhs);
5317 } else {
5318 __ CmpUleD(dst, lhs, rhs);
5319 }
5320 return false;
5321 case kCondGT:
5322 if (gt_bias) {
5323 __ CmpUltD(dst, rhs, lhs);
5324 } else {
5325 __ CmpLtD(dst, rhs, lhs);
5326 }
5327 return false;
5328 case kCondGE:
5329 if (gt_bias) {
5330 __ CmpUleD(dst, rhs, lhs);
5331 } else {
5332 __ CmpLeD(dst, rhs, lhs);
5333 }
5334 return false;
5335 default:
5336 LOG(FATAL) << "Unexpected non-floating-point condition";
5337 UNREACHABLE();
5338 }
5339 }
5340}
5341
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005342void InstructionCodeGeneratorMIPS::GenerateFpCompareAndBranch(IfCondition cond,
5343 bool gt_bias,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005344 DataType::Type type,
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005345 LocationSummary* locations,
5346 MipsLabel* label) {
5347 FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
5348 FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
5349 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005350 if (type == DataType::Type::kFloat32) {
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005351 if (isR6) {
5352 switch (cond) {
5353 case kCondEQ:
5354 __ CmpEqS(FTMP, lhs, rhs);
5355 __ Bc1nez(FTMP, label);
5356 break;
5357 case kCondNE:
5358 __ CmpEqS(FTMP, lhs, rhs);
5359 __ Bc1eqz(FTMP, label);
5360 break;
5361 case kCondLT:
5362 if (gt_bias) {
5363 __ CmpLtS(FTMP, lhs, rhs);
5364 } else {
5365 __ CmpUltS(FTMP, lhs, rhs);
5366 }
5367 __ Bc1nez(FTMP, label);
5368 break;
5369 case kCondLE:
5370 if (gt_bias) {
5371 __ CmpLeS(FTMP, lhs, rhs);
5372 } else {
5373 __ CmpUleS(FTMP, lhs, rhs);
5374 }
5375 __ Bc1nez(FTMP, label);
5376 break;
5377 case kCondGT:
5378 if (gt_bias) {
5379 __ CmpUltS(FTMP, rhs, lhs);
5380 } else {
5381 __ CmpLtS(FTMP, rhs, lhs);
5382 }
5383 __ Bc1nez(FTMP, label);
5384 break;
5385 case kCondGE:
5386 if (gt_bias) {
5387 __ CmpUleS(FTMP, rhs, lhs);
5388 } else {
5389 __ CmpLeS(FTMP, rhs, lhs);
5390 }
5391 __ Bc1nez(FTMP, label);
5392 break;
5393 default:
5394 LOG(FATAL) << "Unexpected non-floating-point condition";
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005395 UNREACHABLE();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005396 }
5397 } else {
5398 switch (cond) {
5399 case kCondEQ:
5400 __ CeqS(0, lhs, rhs);
5401 __ Bc1t(0, label);
5402 break;
5403 case kCondNE:
5404 __ CeqS(0, lhs, rhs);
5405 __ Bc1f(0, label);
5406 break;
5407 case kCondLT:
5408 if (gt_bias) {
5409 __ ColtS(0, lhs, rhs);
5410 } else {
5411 __ CultS(0, lhs, rhs);
5412 }
5413 __ Bc1t(0, label);
5414 break;
5415 case kCondLE:
5416 if (gt_bias) {
5417 __ ColeS(0, lhs, rhs);
5418 } else {
5419 __ CuleS(0, lhs, rhs);
5420 }
5421 __ Bc1t(0, label);
5422 break;
5423 case kCondGT:
5424 if (gt_bias) {
5425 __ CultS(0, rhs, lhs);
5426 } else {
5427 __ ColtS(0, rhs, lhs);
5428 }
5429 __ Bc1t(0, label);
5430 break;
5431 case kCondGE:
5432 if (gt_bias) {
5433 __ CuleS(0, rhs, lhs);
5434 } else {
5435 __ ColeS(0, rhs, lhs);
5436 }
5437 __ Bc1t(0, label);
5438 break;
5439 default:
5440 LOG(FATAL) << "Unexpected non-floating-point condition";
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005441 UNREACHABLE();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005442 }
5443 }
5444 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005445 DCHECK_EQ(type, DataType::Type::kFloat64);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005446 if (isR6) {
5447 switch (cond) {
5448 case kCondEQ:
5449 __ CmpEqD(FTMP, lhs, rhs);
5450 __ Bc1nez(FTMP, label);
5451 break;
5452 case kCondNE:
5453 __ CmpEqD(FTMP, lhs, rhs);
5454 __ Bc1eqz(FTMP, label);
5455 break;
5456 case kCondLT:
5457 if (gt_bias) {
5458 __ CmpLtD(FTMP, lhs, rhs);
5459 } else {
5460 __ CmpUltD(FTMP, lhs, rhs);
5461 }
5462 __ Bc1nez(FTMP, label);
5463 break;
5464 case kCondLE:
5465 if (gt_bias) {
5466 __ CmpLeD(FTMP, lhs, rhs);
5467 } else {
5468 __ CmpUleD(FTMP, lhs, rhs);
5469 }
5470 __ Bc1nez(FTMP, label);
5471 break;
5472 case kCondGT:
5473 if (gt_bias) {
5474 __ CmpUltD(FTMP, rhs, lhs);
5475 } else {
5476 __ CmpLtD(FTMP, rhs, lhs);
5477 }
5478 __ Bc1nez(FTMP, label);
5479 break;
5480 case kCondGE:
5481 if (gt_bias) {
5482 __ CmpUleD(FTMP, rhs, lhs);
5483 } else {
5484 __ CmpLeD(FTMP, rhs, lhs);
5485 }
5486 __ Bc1nez(FTMP, label);
5487 break;
5488 default:
5489 LOG(FATAL) << "Unexpected non-floating-point condition";
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005490 UNREACHABLE();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005491 }
5492 } else {
5493 switch (cond) {
5494 case kCondEQ:
5495 __ CeqD(0, lhs, rhs);
5496 __ Bc1t(0, label);
5497 break;
5498 case kCondNE:
5499 __ CeqD(0, lhs, rhs);
5500 __ Bc1f(0, label);
5501 break;
5502 case kCondLT:
5503 if (gt_bias) {
5504 __ ColtD(0, lhs, rhs);
5505 } else {
5506 __ CultD(0, lhs, rhs);
5507 }
5508 __ Bc1t(0, label);
5509 break;
5510 case kCondLE:
5511 if (gt_bias) {
5512 __ ColeD(0, lhs, rhs);
5513 } else {
5514 __ CuleD(0, lhs, rhs);
5515 }
5516 __ Bc1t(0, label);
5517 break;
5518 case kCondGT:
5519 if (gt_bias) {
5520 __ CultD(0, rhs, lhs);
5521 } else {
5522 __ ColtD(0, rhs, lhs);
5523 }
5524 __ Bc1t(0, label);
5525 break;
5526 case kCondGE:
5527 if (gt_bias) {
5528 __ CuleD(0, rhs, lhs);
5529 } else {
5530 __ ColeD(0, rhs, lhs);
5531 }
5532 __ Bc1t(0, label);
5533 break;
5534 default:
5535 LOG(FATAL) << "Unexpected non-floating-point condition";
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005536 UNREACHABLE();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005537 }
5538 }
5539 }
5540}
5541
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005542void InstructionCodeGeneratorMIPS::GenerateTestAndBranch(HInstruction* instruction,
David Brazdil0debae72015-11-12 18:37:00 +00005543 size_t condition_input_index,
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005544 MipsLabel* true_target,
David Brazdil0debae72015-11-12 18:37:00 +00005545 MipsLabel* false_target) {
5546 HInstruction* cond = instruction->InputAt(condition_input_index);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005547
David Brazdil0debae72015-11-12 18:37:00 +00005548 if (true_target == nullptr && false_target == nullptr) {
5549 // Nothing to do. The code always falls through.
5550 return;
5551 } else if (cond->IsIntConstant()) {
Roland Levillain1a653882016-03-18 18:05:57 +00005552 // Constant condition, statically compared against "true" (integer value 1).
5553 if (cond->AsIntConstant()->IsTrue()) {
David Brazdil0debae72015-11-12 18:37:00 +00005554 if (true_target != nullptr) {
5555 __ B(true_target);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005556 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005557 } else {
Roland Levillain1a653882016-03-18 18:05:57 +00005558 DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
David Brazdil0debae72015-11-12 18:37:00 +00005559 if (false_target != nullptr) {
5560 __ B(false_target);
5561 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005562 }
David Brazdil0debae72015-11-12 18:37:00 +00005563 return;
5564 }
5565
5566 // The following code generates these patterns:
5567 // (1) true_target == nullptr && false_target != nullptr
5568 // - opposite condition true => branch to false_target
5569 // (2) true_target != nullptr && false_target == nullptr
5570 // - condition true => branch to true_target
5571 // (3) true_target != nullptr && false_target != nullptr
5572 // - condition true => branch to true_target
5573 // - branch to false_target
5574 if (IsBooleanValueOrMaterializedCondition(cond)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005575 // The condition instruction has been materialized, compare the output to 0.
David Brazdil0debae72015-11-12 18:37:00 +00005576 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005577 DCHECK(cond_val.IsRegister());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005578 if (true_target == nullptr) {
David Brazdil0debae72015-11-12 18:37:00 +00005579 __ Beqz(cond_val.AsRegister<Register>(), false_target);
5580 } else {
5581 __ Bnez(cond_val.AsRegister<Register>(), true_target);
5582 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005583 } else {
5584 // The condition instruction has not been materialized, use its inputs as
5585 // the comparison and its condition as the branch condition.
David Brazdil0debae72015-11-12 18:37:00 +00005586 HCondition* condition = cond->AsCondition();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005587 DataType::Type type = condition->InputAt(0)->GetType();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005588 LocationSummary* locations = cond->GetLocations();
5589 IfCondition if_cond = condition->GetCondition();
5590 MipsLabel* branch_target = true_target;
David Brazdil0debae72015-11-12 18:37:00 +00005591
David Brazdil0debae72015-11-12 18:37:00 +00005592 if (true_target == nullptr) {
5593 if_cond = condition->GetOppositeCondition();
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005594 branch_target = false_target;
David Brazdil0debae72015-11-12 18:37:00 +00005595 }
5596
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005597 switch (type) {
5598 default:
5599 GenerateIntCompareAndBranch(if_cond, locations, branch_target);
5600 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005601 case DataType::Type::kInt64:
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005602 GenerateLongCompareAndBranch(if_cond, locations, branch_target);
5603 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005604 case DataType::Type::kFloat32:
5605 case DataType::Type::kFloat64:
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08005606 GenerateFpCompareAndBranch(if_cond, condition->IsGtBias(), type, locations, branch_target);
5607 break;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005608 }
5609 }
David Brazdil0debae72015-11-12 18:37:00 +00005610
5611 // If neither branch falls through (case 3), the conditional branch to `true_target`
5612 // was already emitted (case 2) and we need to emit a jump to `false_target`.
5613 if (true_target != nullptr && false_target != nullptr) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005614 __ B(false_target);
5615 }
5616}
5617
5618void LocationsBuilderMIPS::VisitIf(HIf* if_instr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005619 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
David Brazdil0debae72015-11-12 18:37:00 +00005620 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005621 locations->SetInAt(0, Location::RequiresRegister());
5622 }
5623}
5624
5625void InstructionCodeGeneratorMIPS::VisitIf(HIf* if_instr) {
David Brazdil0debae72015-11-12 18:37:00 +00005626 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
5627 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
5628 MipsLabel* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
5629 nullptr : codegen_->GetLabelOf(true_successor);
5630 MipsLabel* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
5631 nullptr : codegen_->GetLabelOf(false_successor);
5632 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005633}
5634
5635void LocationsBuilderMIPS::VisitDeoptimize(HDeoptimize* deoptimize) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005636 LocationSummary* locations = new (GetGraph()->GetAllocator())
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005637 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +01005638 InvokeRuntimeCallingConvention calling_convention;
5639 RegisterSet caller_saves = RegisterSet::Empty();
5640 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
5641 locations->SetCustomSlowPathCallerSaves(caller_saves);
David Brazdil0debae72015-11-12 18:37:00 +00005642 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005643 locations->SetInAt(0, Location::RequiresRegister());
5644 }
5645}
5646
5647void InstructionCodeGeneratorMIPS::VisitDeoptimize(HDeoptimize* deoptimize) {
Aart Bik42249c32016-01-07 15:33:50 -08005648 SlowPathCodeMIPS* slow_path =
5649 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathMIPS>(deoptimize);
David Brazdil0debae72015-11-12 18:37:00 +00005650 GenerateTestAndBranch(deoptimize,
5651 /* condition_input_index */ 0,
5652 slow_path->GetEntryLabel(),
5653 /* false_target */ nullptr);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02005654}
5655
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005656// This function returns true if a conditional move can be generated for HSelect.
5657// Otherwise it returns false and HSelect must be implemented in terms of conditonal
5658// branches and regular moves.
5659//
5660// If `locations_to_set` isn't nullptr, its inputs and outputs are set for HSelect.
5661//
5662// While determining feasibility of a conditional move and setting inputs/outputs
5663// are two distinct tasks, this function does both because they share quite a bit
5664// of common logic.
5665static bool CanMoveConditionally(HSelect* select, bool is_r6, LocationSummary* locations_to_set) {
5666 bool materialized = IsBooleanValueOrMaterializedCondition(select->GetCondition());
5667 HInstruction* cond = select->InputAt(/* condition_input_index */ 2);
5668 HCondition* condition = cond->AsCondition();
5669
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005670 DataType::Type cond_type =
5671 materialized ? DataType::Type::kInt32 : condition->InputAt(0)->GetType();
5672 DataType::Type dst_type = select->GetType();
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005673
5674 HConstant* cst_true_value = select->GetTrueValue()->AsConstant();
5675 HConstant* cst_false_value = select->GetFalseValue()->AsConstant();
5676 bool is_true_value_zero_constant =
5677 (cst_true_value != nullptr && cst_true_value->IsZeroBitPattern());
5678 bool is_false_value_zero_constant =
5679 (cst_false_value != nullptr && cst_false_value->IsZeroBitPattern());
5680
5681 bool can_move_conditionally = false;
5682 bool use_const_for_false_in = false;
5683 bool use_const_for_true_in = false;
5684
5685 if (!cond->IsConstant()) {
5686 switch (cond_type) {
5687 default:
5688 switch (dst_type) {
5689 default:
5690 // Moving int on int condition.
5691 if (is_r6) {
5692 if (is_true_value_zero_constant) {
5693 // seleqz out_reg, false_reg, cond_reg
5694 can_move_conditionally = true;
5695 use_const_for_true_in = true;
5696 } else if (is_false_value_zero_constant) {
5697 // selnez out_reg, true_reg, cond_reg
5698 can_move_conditionally = true;
5699 use_const_for_false_in = true;
5700 } else if (materialized) {
5701 // Not materializing unmaterialized int conditions
5702 // to keep the instruction count low.
5703 // selnez AT, true_reg, cond_reg
5704 // seleqz TMP, false_reg, cond_reg
5705 // or out_reg, AT, TMP
5706 can_move_conditionally = true;
5707 }
5708 } else {
5709 // movn out_reg, true_reg/ZERO, cond_reg
5710 can_move_conditionally = true;
5711 use_const_for_true_in = is_true_value_zero_constant;
5712 }
5713 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005714 case DataType::Type::kInt64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005715 // Moving long on int condition.
5716 if (is_r6) {
5717 if (is_true_value_zero_constant) {
5718 // seleqz out_reg_lo, false_reg_lo, cond_reg
5719 // seleqz out_reg_hi, false_reg_hi, cond_reg
5720 can_move_conditionally = true;
5721 use_const_for_true_in = true;
5722 } else if (is_false_value_zero_constant) {
5723 // selnez out_reg_lo, true_reg_lo, cond_reg
5724 // selnez out_reg_hi, true_reg_hi, cond_reg
5725 can_move_conditionally = true;
5726 use_const_for_false_in = true;
5727 }
5728 // Other long conditional moves would generate 6+ instructions,
5729 // which is too many.
5730 } else {
5731 // movn out_reg_lo, true_reg_lo/ZERO, cond_reg
5732 // movn out_reg_hi, true_reg_hi/ZERO, cond_reg
5733 can_move_conditionally = true;
5734 use_const_for_true_in = is_true_value_zero_constant;
5735 }
5736 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005737 case DataType::Type::kFloat32:
5738 case DataType::Type::kFloat64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005739 // Moving float/double on int condition.
5740 if (is_r6) {
5741 if (materialized) {
5742 // Not materializing unmaterialized int conditions
5743 // to keep the instruction count low.
5744 can_move_conditionally = true;
5745 if (is_true_value_zero_constant) {
5746 // sltu TMP, ZERO, cond_reg
5747 // mtc1 TMP, temp_cond_reg
5748 // seleqz.fmt out_reg, false_reg, temp_cond_reg
5749 use_const_for_true_in = true;
5750 } else if (is_false_value_zero_constant) {
5751 // sltu TMP, ZERO, cond_reg
5752 // mtc1 TMP, temp_cond_reg
5753 // selnez.fmt out_reg, true_reg, temp_cond_reg
5754 use_const_for_false_in = true;
5755 } else {
5756 // sltu TMP, ZERO, cond_reg
5757 // mtc1 TMP, temp_cond_reg
5758 // sel.fmt temp_cond_reg, false_reg, true_reg
5759 // mov.fmt out_reg, temp_cond_reg
5760 }
5761 }
5762 } else {
5763 // movn.fmt out_reg, true_reg, cond_reg
5764 can_move_conditionally = true;
5765 }
5766 break;
5767 }
5768 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005769 case DataType::Type::kInt64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005770 // We don't materialize long comparison now
5771 // and use conditional branches instead.
5772 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005773 case DataType::Type::kFloat32:
5774 case DataType::Type::kFloat64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005775 switch (dst_type) {
5776 default:
5777 // Moving int on float/double condition.
5778 if (is_r6) {
5779 if (is_true_value_zero_constant) {
5780 // mfc1 TMP, temp_cond_reg
5781 // seleqz out_reg, false_reg, TMP
5782 can_move_conditionally = true;
5783 use_const_for_true_in = true;
5784 } else if (is_false_value_zero_constant) {
5785 // mfc1 TMP, temp_cond_reg
5786 // selnez out_reg, true_reg, TMP
5787 can_move_conditionally = true;
5788 use_const_for_false_in = true;
5789 } else {
5790 // mfc1 TMP, temp_cond_reg
5791 // selnez AT, true_reg, TMP
5792 // seleqz TMP, false_reg, TMP
5793 // or out_reg, AT, TMP
5794 can_move_conditionally = true;
5795 }
5796 } else {
5797 // movt out_reg, true_reg/ZERO, cc
5798 can_move_conditionally = true;
5799 use_const_for_true_in = is_true_value_zero_constant;
5800 }
5801 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005802 case DataType::Type::kInt64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005803 // Moving long on float/double condition.
5804 if (is_r6) {
5805 if (is_true_value_zero_constant) {
5806 // mfc1 TMP, temp_cond_reg
5807 // seleqz out_reg_lo, false_reg_lo, TMP
5808 // seleqz out_reg_hi, false_reg_hi, TMP
5809 can_move_conditionally = true;
5810 use_const_for_true_in = true;
5811 } else if (is_false_value_zero_constant) {
5812 // mfc1 TMP, temp_cond_reg
5813 // selnez out_reg_lo, true_reg_lo, TMP
5814 // selnez out_reg_hi, true_reg_hi, TMP
5815 can_move_conditionally = true;
5816 use_const_for_false_in = true;
5817 }
5818 // Other long conditional moves would generate 6+ instructions,
5819 // which is too many.
5820 } else {
5821 // movt out_reg_lo, true_reg_lo/ZERO, cc
5822 // movt out_reg_hi, true_reg_hi/ZERO, cc
5823 can_move_conditionally = true;
5824 use_const_for_true_in = is_true_value_zero_constant;
5825 }
5826 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005827 case DataType::Type::kFloat32:
5828 case DataType::Type::kFloat64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005829 // Moving float/double on float/double condition.
5830 if (is_r6) {
5831 can_move_conditionally = true;
5832 if (is_true_value_zero_constant) {
5833 // seleqz.fmt out_reg, false_reg, temp_cond_reg
5834 use_const_for_true_in = true;
5835 } else if (is_false_value_zero_constant) {
5836 // selnez.fmt out_reg, true_reg, temp_cond_reg
5837 use_const_for_false_in = true;
5838 } else {
5839 // sel.fmt temp_cond_reg, false_reg, true_reg
5840 // mov.fmt out_reg, temp_cond_reg
5841 }
5842 } else {
5843 // movt.fmt out_reg, true_reg, cc
5844 can_move_conditionally = true;
5845 }
5846 break;
5847 }
5848 break;
5849 }
5850 }
5851
5852 if (can_move_conditionally) {
5853 DCHECK(!use_const_for_false_in || !use_const_for_true_in);
5854 } else {
5855 DCHECK(!use_const_for_false_in);
5856 DCHECK(!use_const_for_true_in);
5857 }
5858
5859 if (locations_to_set != nullptr) {
5860 if (use_const_for_false_in) {
5861 locations_to_set->SetInAt(0, Location::ConstantLocation(cst_false_value));
5862 } else {
5863 locations_to_set->SetInAt(0,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005864 DataType::IsFloatingPointType(dst_type)
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005865 ? Location::RequiresFpuRegister()
5866 : Location::RequiresRegister());
5867 }
5868 if (use_const_for_true_in) {
5869 locations_to_set->SetInAt(1, Location::ConstantLocation(cst_true_value));
5870 } else {
5871 locations_to_set->SetInAt(1,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005872 DataType::IsFloatingPointType(dst_type)
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005873 ? Location::RequiresFpuRegister()
5874 : Location::RequiresRegister());
5875 }
5876 if (materialized) {
5877 locations_to_set->SetInAt(2, Location::RequiresRegister());
5878 }
5879 // On R6 we don't require the output to be the same as the
5880 // first input for conditional moves unlike on R2.
5881 bool is_out_same_as_first_in = !can_move_conditionally || !is_r6;
5882 if (is_out_same_as_first_in) {
5883 locations_to_set->SetOut(Location::SameAsFirstInput());
5884 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005885 locations_to_set->SetOut(DataType::IsFloatingPointType(dst_type)
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005886 ? Location::RequiresFpuRegister()
5887 : Location::RequiresRegister());
5888 }
5889 }
5890
5891 return can_move_conditionally;
5892}
5893
5894void InstructionCodeGeneratorMIPS::GenConditionalMoveR2(HSelect* select) {
5895 LocationSummary* locations = select->GetLocations();
5896 Location dst = locations->Out();
5897 Location src = locations->InAt(1);
5898 Register src_reg = ZERO;
5899 Register src_reg_high = ZERO;
5900 HInstruction* cond = select->InputAt(/* condition_input_index */ 2);
5901 Register cond_reg = TMP;
5902 int cond_cc = 0;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005903 DataType::Type cond_type = DataType::Type::kInt32;
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005904 bool cond_inverted = false;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005905 DataType::Type dst_type = select->GetType();
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005906
5907 if (IsBooleanValueOrMaterializedCondition(cond)) {
5908 cond_reg = locations->InAt(/* condition_input_index */ 2).AsRegister<Register>();
5909 } else {
5910 HCondition* condition = cond->AsCondition();
5911 LocationSummary* cond_locations = cond->GetLocations();
5912 IfCondition if_cond = condition->GetCondition();
5913 cond_type = condition->InputAt(0)->GetType();
5914 switch (cond_type) {
5915 default:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005916 DCHECK_NE(cond_type, DataType::Type::kInt64);
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005917 cond_inverted = MaterializeIntCompare(if_cond, cond_locations, cond_reg);
5918 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005919 case DataType::Type::kFloat32:
5920 case DataType::Type::kFloat64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005921 cond_inverted = MaterializeFpCompareR2(if_cond,
5922 condition->IsGtBias(),
5923 cond_type,
5924 cond_locations,
5925 cond_cc);
5926 break;
5927 }
5928 }
5929
5930 DCHECK(dst.Equals(locations->InAt(0)));
5931 if (src.IsRegister()) {
5932 src_reg = src.AsRegister<Register>();
5933 } else if (src.IsRegisterPair()) {
5934 src_reg = src.AsRegisterPairLow<Register>();
5935 src_reg_high = src.AsRegisterPairHigh<Register>();
5936 } else if (src.IsConstant()) {
5937 DCHECK(src.GetConstant()->IsZeroBitPattern());
5938 }
5939
5940 switch (cond_type) {
5941 default:
5942 switch (dst_type) {
5943 default:
5944 if (cond_inverted) {
5945 __ Movz(dst.AsRegister<Register>(), src_reg, cond_reg);
5946 } else {
5947 __ Movn(dst.AsRegister<Register>(), src_reg, cond_reg);
5948 }
5949 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005950 case DataType::Type::kInt64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005951 if (cond_inverted) {
5952 __ Movz(dst.AsRegisterPairLow<Register>(), src_reg, cond_reg);
5953 __ Movz(dst.AsRegisterPairHigh<Register>(), src_reg_high, cond_reg);
5954 } else {
5955 __ Movn(dst.AsRegisterPairLow<Register>(), src_reg, cond_reg);
5956 __ Movn(dst.AsRegisterPairHigh<Register>(), src_reg_high, cond_reg);
5957 }
5958 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005959 case DataType::Type::kFloat32:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005960 if (cond_inverted) {
5961 __ MovzS(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_reg);
5962 } else {
5963 __ MovnS(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_reg);
5964 }
5965 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005966 case DataType::Type::kFloat64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005967 if (cond_inverted) {
5968 __ MovzD(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_reg);
5969 } else {
5970 __ MovnD(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_reg);
5971 }
5972 break;
5973 }
5974 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005975 case DataType::Type::kInt64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005976 LOG(FATAL) << "Unreachable";
5977 UNREACHABLE();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005978 case DataType::Type::kFloat32:
5979 case DataType::Type::kFloat64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005980 switch (dst_type) {
5981 default:
5982 if (cond_inverted) {
5983 __ Movf(dst.AsRegister<Register>(), src_reg, cond_cc);
5984 } else {
5985 __ Movt(dst.AsRegister<Register>(), src_reg, cond_cc);
5986 }
5987 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005988 case DataType::Type::kInt64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005989 if (cond_inverted) {
5990 __ Movf(dst.AsRegisterPairLow<Register>(), src_reg, cond_cc);
5991 __ Movf(dst.AsRegisterPairHigh<Register>(), src_reg_high, cond_cc);
5992 } else {
5993 __ Movt(dst.AsRegisterPairLow<Register>(), src_reg, cond_cc);
5994 __ Movt(dst.AsRegisterPairHigh<Register>(), src_reg_high, cond_cc);
5995 }
5996 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005997 case DataType::Type::kFloat32:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07005998 if (cond_inverted) {
5999 __ MovfS(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_cc);
6000 } else {
6001 __ MovtS(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_cc);
6002 }
6003 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006004 case DataType::Type::kFloat64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006005 if (cond_inverted) {
6006 __ MovfD(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_cc);
6007 } else {
6008 __ MovtD(dst.AsFpuRegister<FRegister>(), src.AsFpuRegister<FRegister>(), cond_cc);
6009 }
6010 break;
6011 }
6012 break;
6013 }
6014}
6015
6016void InstructionCodeGeneratorMIPS::GenConditionalMoveR6(HSelect* select) {
6017 LocationSummary* locations = select->GetLocations();
6018 Location dst = locations->Out();
6019 Location false_src = locations->InAt(0);
6020 Location true_src = locations->InAt(1);
6021 HInstruction* cond = select->InputAt(/* condition_input_index */ 2);
6022 Register cond_reg = TMP;
6023 FRegister fcond_reg = FTMP;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006024 DataType::Type cond_type = DataType::Type::kInt32;
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006025 bool cond_inverted = false;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006026 DataType::Type dst_type = select->GetType();
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006027
6028 if (IsBooleanValueOrMaterializedCondition(cond)) {
6029 cond_reg = locations->InAt(/* condition_input_index */ 2).AsRegister<Register>();
6030 } else {
6031 HCondition* condition = cond->AsCondition();
6032 LocationSummary* cond_locations = cond->GetLocations();
6033 IfCondition if_cond = condition->GetCondition();
6034 cond_type = condition->InputAt(0)->GetType();
6035 switch (cond_type) {
6036 default:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006037 DCHECK_NE(cond_type, DataType::Type::kInt64);
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006038 cond_inverted = MaterializeIntCompare(if_cond, cond_locations, cond_reg);
6039 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006040 case DataType::Type::kFloat32:
6041 case DataType::Type::kFloat64:
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006042 cond_inverted = MaterializeFpCompareR6(if_cond,
6043 condition->IsGtBias(),
6044 cond_type,
6045 cond_locations,
6046 fcond_reg);
6047 break;
6048 }
6049 }
6050
6051 if (true_src.IsConstant()) {
6052 DCHECK(true_src.GetConstant()->IsZeroBitPattern());
6053 }
6054 if (false_src.IsConstant()) {
6055 DCHECK(false_src.GetConstant()->IsZeroBitPattern());
6056 }
6057
6058 switch (dst_type) {
6059 default:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006060 if (DataType::IsFloatingPointType(cond_type)) {
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006061 __ Mfc1(cond_reg, fcond_reg);
6062 }
6063 if (true_src.IsConstant()) {
6064 if (cond_inverted) {
6065 __ Selnez(dst.AsRegister<Register>(), false_src.AsRegister<Register>(), cond_reg);
6066 } else {
6067 __ Seleqz(dst.AsRegister<Register>(), false_src.AsRegister<Register>(), cond_reg);
6068 }
6069 } else if (false_src.IsConstant()) {
6070 if (cond_inverted) {
6071 __ Seleqz(dst.AsRegister<Register>(), true_src.AsRegister<Register>(), cond_reg);
6072 } else {
6073 __ Selnez(dst.AsRegister<Register>(), true_src.AsRegister<Register>(), cond_reg);
6074 }
6075 } else {
6076 DCHECK_NE(cond_reg, AT);
6077 if (cond_inverted) {
6078 __ Seleqz(AT, true_src.AsRegister<Register>(), cond_reg);
6079 __ Selnez(TMP, false_src.AsRegister<Register>(), cond_reg);
6080 } else {
6081 __ Selnez(AT, true_src.AsRegister<Register>(), cond_reg);
6082 __ Seleqz(TMP, false_src.AsRegister<Register>(), cond_reg);
6083 }
6084 __ Or(dst.AsRegister<Register>(), AT, TMP);
6085 }
6086 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006087 case DataType::Type::kInt64: {
6088 if (DataType::IsFloatingPointType(cond_type)) {
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006089 __ Mfc1(cond_reg, fcond_reg);
6090 }
6091 Register dst_lo = dst.AsRegisterPairLow<Register>();
6092 Register dst_hi = dst.AsRegisterPairHigh<Register>();
6093 if (true_src.IsConstant()) {
6094 Register src_lo = false_src.AsRegisterPairLow<Register>();
6095 Register src_hi = false_src.AsRegisterPairHigh<Register>();
6096 if (cond_inverted) {
6097 __ Selnez(dst_lo, src_lo, cond_reg);
6098 __ Selnez(dst_hi, src_hi, cond_reg);
6099 } else {
6100 __ Seleqz(dst_lo, src_lo, cond_reg);
6101 __ Seleqz(dst_hi, src_hi, cond_reg);
6102 }
6103 } else {
6104 DCHECK(false_src.IsConstant());
6105 Register src_lo = true_src.AsRegisterPairLow<Register>();
6106 Register src_hi = true_src.AsRegisterPairHigh<Register>();
6107 if (cond_inverted) {
6108 __ Seleqz(dst_lo, src_lo, cond_reg);
6109 __ Seleqz(dst_hi, src_hi, cond_reg);
6110 } else {
6111 __ Selnez(dst_lo, src_lo, cond_reg);
6112 __ Selnez(dst_hi, src_hi, cond_reg);
6113 }
6114 }
6115 break;
6116 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006117 case DataType::Type::kFloat32: {
6118 if (!DataType::IsFloatingPointType(cond_type)) {
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006119 // sel*.fmt tests bit 0 of the condition register, account for that.
6120 __ Sltu(TMP, ZERO, cond_reg);
6121 __ Mtc1(TMP, fcond_reg);
6122 }
6123 FRegister dst_reg = dst.AsFpuRegister<FRegister>();
6124 if (true_src.IsConstant()) {
6125 FRegister src_reg = false_src.AsFpuRegister<FRegister>();
6126 if (cond_inverted) {
6127 __ SelnezS(dst_reg, src_reg, fcond_reg);
6128 } else {
6129 __ SeleqzS(dst_reg, src_reg, fcond_reg);
6130 }
6131 } else if (false_src.IsConstant()) {
6132 FRegister src_reg = true_src.AsFpuRegister<FRegister>();
6133 if (cond_inverted) {
6134 __ SeleqzS(dst_reg, src_reg, fcond_reg);
6135 } else {
6136 __ SelnezS(dst_reg, src_reg, fcond_reg);
6137 }
6138 } else {
6139 if (cond_inverted) {
6140 __ SelS(fcond_reg,
6141 true_src.AsFpuRegister<FRegister>(),
6142 false_src.AsFpuRegister<FRegister>());
6143 } else {
6144 __ SelS(fcond_reg,
6145 false_src.AsFpuRegister<FRegister>(),
6146 true_src.AsFpuRegister<FRegister>());
6147 }
6148 __ MovS(dst_reg, fcond_reg);
6149 }
6150 break;
6151 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006152 case DataType::Type::kFloat64: {
6153 if (!DataType::IsFloatingPointType(cond_type)) {
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006154 // sel*.fmt tests bit 0 of the condition register, account for that.
6155 __ Sltu(TMP, ZERO, cond_reg);
6156 __ Mtc1(TMP, fcond_reg);
6157 }
6158 FRegister dst_reg = dst.AsFpuRegister<FRegister>();
6159 if (true_src.IsConstant()) {
6160 FRegister src_reg = false_src.AsFpuRegister<FRegister>();
6161 if (cond_inverted) {
6162 __ SelnezD(dst_reg, src_reg, fcond_reg);
6163 } else {
6164 __ SeleqzD(dst_reg, src_reg, fcond_reg);
6165 }
6166 } else if (false_src.IsConstant()) {
6167 FRegister src_reg = true_src.AsFpuRegister<FRegister>();
6168 if (cond_inverted) {
6169 __ SeleqzD(dst_reg, src_reg, fcond_reg);
6170 } else {
6171 __ SelnezD(dst_reg, src_reg, fcond_reg);
6172 }
6173 } else {
6174 if (cond_inverted) {
6175 __ SelD(fcond_reg,
6176 true_src.AsFpuRegister<FRegister>(),
6177 false_src.AsFpuRegister<FRegister>());
6178 } else {
6179 __ SelD(fcond_reg,
6180 false_src.AsFpuRegister<FRegister>(),
6181 true_src.AsFpuRegister<FRegister>());
6182 }
6183 __ MovD(dst_reg, fcond_reg);
6184 }
6185 break;
6186 }
6187 }
6188}
6189
Goran Jakovljevicc6418422016-12-05 16:31:55 +01006190void LocationsBuilderMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01006191 LocationSummary* locations = new (GetGraph()->GetAllocator())
Goran Jakovljevicc6418422016-12-05 16:31:55 +01006192 LocationSummary(flag, LocationSummary::kNoCall);
6193 locations->SetOut(Location::RequiresRegister());
Mingyao Yang063fc772016-08-02 11:02:54 -07006194}
6195
Goran Jakovljevicc6418422016-12-05 16:31:55 +01006196void InstructionCodeGeneratorMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
6197 __ LoadFromOffset(kLoadWord,
6198 flag->GetLocations()->Out().AsRegister<Register>(),
6199 SP,
6200 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
Mingyao Yang063fc772016-08-02 11:02:54 -07006201}
6202
David Brazdil74eb1b22015-12-14 11:44:01 +00006203void LocationsBuilderMIPS::VisitSelect(HSelect* select) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01006204 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006205 CanMoveConditionally(select, codegen_->GetInstructionSetFeatures().IsR6(), locations);
David Brazdil74eb1b22015-12-14 11:44:01 +00006206}
6207
6208void InstructionCodeGeneratorMIPS::VisitSelect(HSelect* select) {
Alexey Frunze674b9ee2016-09-20 14:54:15 -07006209 bool is_r6 = codegen_->GetInstructionSetFeatures().IsR6();
6210 if (CanMoveConditionally(select, is_r6, /* locations_to_set */ nullptr)) {
6211 if (is_r6) {
6212 GenConditionalMoveR6(select);
6213 } else {
6214 GenConditionalMoveR2(select);
6215 }
6216 } else {
6217 LocationSummary* locations = select->GetLocations();
6218 MipsLabel false_target;
6219 GenerateTestAndBranch(select,
6220 /* condition_input_index */ 2,
6221 /* true_target */ nullptr,
6222 &false_target);
6223 codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
6224 __ Bind(&false_target);
6225 }
David Brazdil74eb1b22015-12-14 11:44:01 +00006226}
6227
David Srbecky0cf44932015-12-09 14:09:59 +00006228void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01006229 new (GetGraph()->GetAllocator()) LocationSummary(info);
David Srbecky0cf44932015-12-09 14:09:59 +00006230}
6231
David Srbeckyd28f4a02016-03-14 17:14:24 +00006232void InstructionCodeGeneratorMIPS::VisitNativeDebugInfo(HNativeDebugInfo*) {
6233 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
David Srbeckyc7098ff2016-02-09 14:30:11 +00006234}
6235
6236void CodeGeneratorMIPS::GenerateNop() {
6237 __ Nop();
David Srbecky0cf44932015-12-09 14:09:59 +00006238}
6239
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006240void LocationsBuilderMIPS::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006241 DataType::Type field_type = field_info.GetFieldType();
6242 bool is_wide = (field_type == DataType::Type::kInt64) || (field_type == DataType::Type::kFloat64);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006243 bool generate_volatile = field_info.IsVolatile() && is_wide;
Alexey Frunze15958152017-02-09 19:08:30 -08006244 bool object_field_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006245 kEmitCompilerReadBarrier && (field_type == DataType::Type::kReference);
Vladimir Markoca6fff82017-10-03 14:49:14 +01006246 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
Alexey Frunze15958152017-02-09 19:08:30 -08006247 instruction,
6248 generate_volatile
6249 ? LocationSummary::kCallOnMainOnly
6250 : (object_field_get_with_read_barrier
6251 ? LocationSummary::kCallOnSlowPath
6252 : LocationSummary::kNoCall));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006253
Alexey Frunzec61c0762017-04-10 13:54:23 -07006254 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
6255 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
6256 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006257 locations->SetInAt(0, Location::RequiresRegister());
6258 if (generate_volatile) {
6259 InvokeRuntimeCallingConvention calling_convention;
6260 // need A0 to hold base + offset
6261 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006262 if (field_type == DataType::Type::kInt64) {
6263 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kInt64));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006264 } else {
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006265 // Use Location::Any() to prevent situations when running out of available fp registers.
6266 locations->SetOut(Location::Any());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006267 // Need some temp core regs since FP results are returned in core registers
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006268 Location reg = calling_convention.GetReturnLocation(DataType::Type::kInt64);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006269 locations->AddTemp(Location::RegisterLocation(reg.AsRegisterPairLow<Register>()));
6270 locations->AddTemp(Location::RegisterLocation(reg.AsRegisterPairHigh<Register>()));
6271 }
6272 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006273 if (DataType::IsFloatingPointType(instruction->GetType())) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006274 locations->SetOut(Location::RequiresFpuRegister());
6275 } else {
Alexey Frunze15958152017-02-09 19:08:30 -08006276 // The output overlaps in the case of an object field get with
6277 // read barriers enabled: we do not want the move to overwrite the
6278 // object's location, as we need it to emit the read barrier.
6279 locations->SetOut(Location::RequiresRegister(),
6280 object_field_get_with_read_barrier
6281 ? Location::kOutputOverlap
6282 : Location::kNoOutputOverlap);
6283 }
6284 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
6285 // We need a temporary register for the read barrier marking slow
6286 // path in CodeGeneratorMIPS::GenerateFieldLoadWithBakerReadBarrier.
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006287 if (!kBakerReadBarrierThunksEnableForFields) {
6288 locations->AddTemp(Location::RequiresRegister());
6289 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006290 }
6291 }
6292}
6293
6294void InstructionCodeGeneratorMIPS::HandleFieldGet(HInstruction* instruction,
6295 const FieldInfo& field_info,
6296 uint32_t dex_pc) {
Vladimir Marko61b92282017-10-11 13:23:17 +01006297 DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
6298 DataType::Type type = instruction->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006299 LocationSummary* locations = instruction->GetLocations();
Alexey Frunze15958152017-02-09 19:08:30 -08006300 Location obj_loc = locations->InAt(0);
6301 Register obj = obj_loc.AsRegister<Register>();
6302 Location dst_loc = locations->Out();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006303 LoadOperandType load_type = kLoadUnsignedByte;
6304 bool is_volatile = field_info.IsVolatile();
Goran Jakovljevic73a42652015-11-20 17:22:57 +01006305 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
Tijana Jakovljevic57433862017-01-17 16:59:03 +01006306 auto null_checker = GetImplicitNullChecker(instruction, codegen_);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006307
6308 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006309 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006310 case DataType::Type::kUint8:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006311 load_type = kLoadUnsignedByte;
6312 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006313 case DataType::Type::kInt8:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006314 load_type = kLoadSignedByte;
6315 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006316 case DataType::Type::kUint16:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006317 load_type = kLoadUnsignedHalfword;
6318 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006319 case DataType::Type::kInt16:
6320 load_type = kLoadSignedHalfword;
6321 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006322 case DataType::Type::kInt32:
6323 case DataType::Type::kFloat32:
6324 case DataType::Type::kReference:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006325 load_type = kLoadWord;
6326 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006327 case DataType::Type::kInt64:
6328 case DataType::Type::kFloat64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006329 load_type = kLoadDoubleword;
6330 break;
Aart Bik66c158e2018-01-31 12:55:04 -08006331 case DataType::Type::kUint32:
6332 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006333 case DataType::Type::kVoid:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006334 LOG(FATAL) << "Unreachable type " << type;
6335 UNREACHABLE();
6336 }
6337
6338 if (is_volatile && load_type == kLoadDoubleword) {
6339 InvokeRuntimeCallingConvention calling_convention;
Goran Jakovljevic73a42652015-11-20 17:22:57 +01006340 __ Addiu32(locations->GetTemp(0).AsRegister<Register>(), obj, offset);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006341 // Do implicit Null check
Goran Jakovljevic2e61a572017-10-23 08:58:15 +02006342 __ LoadFromOffset(kLoadWord,
6343 ZERO,
6344 locations->GetTemp(0).AsRegister<Register>(),
6345 0,
6346 null_checker);
Serban Constantinescufca16662016-07-14 09:21:59 +01006347 codegen_->InvokeRuntime(kQuickA64Load, instruction, dex_pc);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006348 CheckEntrypointTypes<kQuickA64Load, int64_t, volatile const int64_t*>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006349 if (type == DataType::Type::kFloat64) {
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006350 // FP results are returned in core registers. Need to move them.
Alexey Frunze15958152017-02-09 19:08:30 -08006351 if (dst_loc.IsFpuRegister()) {
6352 __ Mtc1(locations->GetTemp(1).AsRegister<Register>(), dst_loc.AsFpuRegister<FRegister>());
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006353 __ MoveToFpuHigh(locations->GetTemp(2).AsRegister<Register>(),
Alexey Frunze15958152017-02-09 19:08:30 -08006354 dst_loc.AsFpuRegister<FRegister>());
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006355 } else {
Alexey Frunze15958152017-02-09 19:08:30 -08006356 DCHECK(dst_loc.IsDoubleStackSlot());
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006357 __ StoreToOffset(kStoreWord,
6358 locations->GetTemp(1).AsRegister<Register>(),
6359 SP,
Alexey Frunze15958152017-02-09 19:08:30 -08006360 dst_loc.GetStackIndex());
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006361 __ StoreToOffset(kStoreWord,
6362 locations->GetTemp(2).AsRegister<Register>(),
6363 SP,
Alexey Frunze15958152017-02-09 19:08:30 -08006364 dst_loc.GetStackIndex() + 4);
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006365 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006366 }
6367 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006368 if (type == DataType::Type::kReference) {
Alexey Frunze15958152017-02-09 19:08:30 -08006369 // /* HeapReference<Object> */ dst = *(obj + offset)
6370 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006371 Location temp_loc =
6372 kBakerReadBarrierThunksEnableForFields ? Location::NoLocation() : locations->GetTemp(0);
Alexey Frunze15958152017-02-09 19:08:30 -08006373 // Note that a potential implicit null check is handled in this
6374 // CodeGeneratorMIPS::GenerateFieldLoadWithBakerReadBarrier call.
6375 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6376 dst_loc,
6377 obj,
6378 offset,
6379 temp_loc,
6380 /* needs_null_check */ true);
6381 if (is_volatile) {
6382 GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6383 }
6384 } else {
6385 __ LoadFromOffset(kLoadWord, dst_loc.AsRegister<Register>(), obj, offset, null_checker);
6386 if (is_volatile) {
6387 GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6388 }
6389 // If read barriers are enabled, emit read barriers other than
6390 // Baker's using a slow path (and also unpoison the loaded
6391 // reference, if heap poisoning is enabled).
6392 codegen_->MaybeGenerateReadBarrierSlow(instruction, dst_loc, dst_loc, obj_loc, offset);
6393 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006394 } else if (!DataType::IsFloatingPointType(type)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006395 Register dst;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006396 if (type == DataType::Type::kInt64) {
Alexey Frunze15958152017-02-09 19:08:30 -08006397 DCHECK(dst_loc.IsRegisterPair());
6398 dst = dst_loc.AsRegisterPairLow<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006399 } else {
Alexey Frunze15958152017-02-09 19:08:30 -08006400 DCHECK(dst_loc.IsRegister());
6401 dst = dst_loc.AsRegister<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006402 }
Alexey Frunze2923db72016-08-20 01:55:47 -07006403 __ LoadFromOffset(load_type, dst, obj, offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006404 } else {
Alexey Frunze15958152017-02-09 19:08:30 -08006405 DCHECK(dst_loc.IsFpuRegister());
6406 FRegister dst = dst_loc.AsFpuRegister<FRegister>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006407 if (type == DataType::Type::kFloat32) {
Alexey Frunze2923db72016-08-20 01:55:47 -07006408 __ LoadSFromOffset(dst, obj, offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006409 } else {
Alexey Frunze2923db72016-08-20 01:55:47 -07006410 __ LoadDFromOffset(dst, obj, offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006411 }
6412 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006413 }
6414
Alexey Frunze15958152017-02-09 19:08:30 -08006415 // Memory barriers, in the case of references, are handled in the
6416 // previous switch statement.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006417 if (is_volatile && (type != DataType::Type::kReference)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006418 GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6419 }
6420}
6421
6422void LocationsBuilderMIPS::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006423 DataType::Type field_type = field_info.GetFieldType();
6424 bool is_wide = (field_type == DataType::Type::kInt64) || (field_type == DataType::Type::kFloat64);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006425 bool generate_volatile = field_info.IsVolatile() && is_wide;
Vladimir Markoca6fff82017-10-03 14:49:14 +01006426 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
Serban Constantinescu54ff4822016-07-07 18:03:19 +01006427 instruction, generate_volatile ? LocationSummary::kCallOnMainOnly : LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006428
6429 locations->SetInAt(0, Location::RequiresRegister());
6430 if (generate_volatile) {
6431 InvokeRuntimeCallingConvention calling_convention;
6432 // need A0 to hold base + offset
6433 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006434 if (field_type == DataType::Type::kInt64) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006435 locations->SetInAt(1, Location::RegisterPairLocation(
6436 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
6437 } else {
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006438 // Use Location::Any() to prevent situations when running out of available fp registers.
6439 locations->SetInAt(1, Location::Any());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006440 // Pass FP parameters in core registers.
6441 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
6442 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
6443 }
6444 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006445 if (DataType::IsFloatingPointType(field_type)) {
Alexey Frunzef58b2482016-09-02 22:14:06 -07006446 locations->SetInAt(1, FpuRegisterOrConstantForStore(instruction->InputAt(1)));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006447 } else {
Alexey Frunzef58b2482016-09-02 22:14:06 -07006448 locations->SetInAt(1, RegisterOrZeroConstant(instruction->InputAt(1)));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006449 }
6450 }
6451}
6452
6453void InstructionCodeGeneratorMIPS::HandleFieldSet(HInstruction* instruction,
6454 const FieldInfo& field_info,
Goran Jakovljevice114da22016-12-26 14:21:43 +01006455 uint32_t dex_pc,
6456 bool value_can_be_null) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006457 DataType::Type type = field_info.GetFieldType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006458 LocationSummary* locations = instruction->GetLocations();
6459 Register obj = locations->InAt(0).AsRegister<Register>();
Alexey Frunzef58b2482016-09-02 22:14:06 -07006460 Location value_location = locations->InAt(1);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006461 StoreOperandType store_type = kStoreByte;
6462 bool is_volatile = field_info.IsVolatile();
Goran Jakovljevic73a42652015-11-20 17:22:57 +01006463 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
Alexey Frunzec061de12017-02-14 13:27:23 -08006464 bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1));
Tijana Jakovljevic57433862017-01-17 16:59:03 +01006465 auto null_checker = GetImplicitNullChecker(instruction, codegen_);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006466
6467 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006468 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006469 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006470 case DataType::Type::kInt8:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006471 store_type = kStoreByte;
6472 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006473 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006474 case DataType::Type::kInt16:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006475 store_type = kStoreHalfword;
6476 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006477 case DataType::Type::kInt32:
6478 case DataType::Type::kFloat32:
6479 case DataType::Type::kReference:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006480 store_type = kStoreWord;
6481 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006482 case DataType::Type::kInt64:
6483 case DataType::Type::kFloat64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006484 store_type = kStoreDoubleword;
6485 break;
Aart Bik66c158e2018-01-31 12:55:04 -08006486 case DataType::Type::kUint32:
6487 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006488 case DataType::Type::kVoid:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006489 LOG(FATAL) << "Unreachable type " << type;
6490 UNREACHABLE();
6491 }
6492
6493 if (is_volatile) {
6494 GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
6495 }
6496
6497 if (is_volatile && store_type == kStoreDoubleword) {
6498 InvokeRuntimeCallingConvention calling_convention;
Goran Jakovljevic73a42652015-11-20 17:22:57 +01006499 __ Addiu32(locations->GetTemp(0).AsRegister<Register>(), obj, offset);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006500 // Do implicit Null check.
Goran Jakovljevic2e61a572017-10-23 08:58:15 +02006501 __ LoadFromOffset(kLoadWord,
6502 ZERO,
6503 locations->GetTemp(0).AsRegister<Register>(),
6504 0,
6505 null_checker);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006506 if (type == DataType::Type::kFloat64) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006507 // Pass FP parameters in core registers.
Alexey Frunzef58b2482016-09-02 22:14:06 -07006508 if (value_location.IsFpuRegister()) {
6509 __ Mfc1(locations->GetTemp(1).AsRegister<Register>(),
6510 value_location.AsFpuRegister<FRegister>());
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006511 __ MoveFromFpuHigh(locations->GetTemp(2).AsRegister<Register>(),
Alexey Frunzef58b2482016-09-02 22:14:06 -07006512 value_location.AsFpuRegister<FRegister>());
6513 } else if (value_location.IsDoubleStackSlot()) {
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006514 __ LoadFromOffset(kLoadWord,
6515 locations->GetTemp(1).AsRegister<Register>(),
6516 SP,
Alexey Frunzef58b2482016-09-02 22:14:06 -07006517 value_location.GetStackIndex());
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006518 __ LoadFromOffset(kLoadWord,
6519 locations->GetTemp(2).AsRegister<Register>(),
6520 SP,
Alexey Frunzef58b2482016-09-02 22:14:06 -07006521 value_location.GetStackIndex() + 4);
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006522 } else {
Alexey Frunzef58b2482016-09-02 22:14:06 -07006523 DCHECK(value_location.IsConstant());
6524 DCHECK(value_location.GetConstant()->IsDoubleConstant());
6525 int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
Goran Jakovljeviccdd822f2016-07-22 09:46:43 +02006526 __ LoadConst64(locations->GetTemp(2).AsRegister<Register>(),
6527 locations->GetTemp(1).AsRegister<Register>(),
6528 value);
6529 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006530 }
Serban Constantinescufca16662016-07-14 09:21:59 +01006531 codegen_->InvokeRuntime(kQuickA64Store, instruction, dex_pc);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006532 CheckEntrypointTypes<kQuickA64Store, void, volatile int64_t *, int64_t>();
6533 } else {
Alexey Frunzef58b2482016-09-02 22:14:06 -07006534 if (value_location.IsConstant()) {
6535 int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
6536 __ StoreConstToOffset(store_type, value, obj, offset, TMP, null_checker);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006537 } else if (!DataType::IsFloatingPointType(type)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006538 Register src;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006539 if (type == DataType::Type::kInt64) {
Alexey Frunzef58b2482016-09-02 22:14:06 -07006540 src = value_location.AsRegisterPairLow<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006541 } else {
Alexey Frunzef58b2482016-09-02 22:14:06 -07006542 src = value_location.AsRegister<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006543 }
Alexey Frunzec061de12017-02-14 13:27:23 -08006544 if (kPoisonHeapReferences && needs_write_barrier) {
6545 // Note that in the case where `value` is a null reference,
6546 // we do not enter this block, as a null reference does not
6547 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006548 DCHECK_EQ(type, DataType::Type::kReference);
Alexey Frunzec061de12017-02-14 13:27:23 -08006549 __ PoisonHeapReference(TMP, src);
6550 __ StoreToOffset(store_type, TMP, obj, offset, null_checker);
6551 } else {
6552 __ StoreToOffset(store_type, src, obj, offset, null_checker);
6553 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006554 } else {
Alexey Frunzef58b2482016-09-02 22:14:06 -07006555 FRegister src = value_location.AsFpuRegister<FRegister>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006556 if (type == DataType::Type::kFloat32) {
Alexey Frunze2923db72016-08-20 01:55:47 -07006557 __ StoreSToOffset(src, obj, offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006558 } else {
Alexey Frunze2923db72016-08-20 01:55:47 -07006559 __ StoreDToOffset(src, obj, offset, null_checker);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006560 }
6561 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006562 }
6563
Alexey Frunzec061de12017-02-14 13:27:23 -08006564 if (needs_write_barrier) {
Alexey Frunzef58b2482016-09-02 22:14:06 -07006565 Register src = value_location.AsRegister<Register>();
Goran Jakovljevice114da22016-12-26 14:21:43 +01006566 codegen_->MarkGCCard(obj, src, value_can_be_null);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006567 }
6568
6569 if (is_volatile) {
6570 GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
6571 }
6572}
6573
6574void LocationsBuilderMIPS::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6575 HandleFieldGet(instruction, instruction->GetFieldInfo());
6576}
6577
6578void InstructionCodeGeneratorMIPS::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6579 HandleFieldGet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc());
6580}
6581
6582void LocationsBuilderMIPS::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6583 HandleFieldSet(instruction, instruction->GetFieldInfo());
6584}
6585
6586void InstructionCodeGeneratorMIPS::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
Goran Jakovljevice114da22016-12-26 14:21:43 +01006587 HandleFieldSet(instruction,
6588 instruction->GetFieldInfo(),
6589 instruction->GetDexPc(),
6590 instruction->GetValueCanBeNull());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02006591}
6592
Alexey Frunze15958152017-02-09 19:08:30 -08006593void InstructionCodeGeneratorMIPS::GenerateReferenceLoadOneRegister(
6594 HInstruction* instruction,
6595 Location out,
6596 uint32_t offset,
6597 Location maybe_temp,
6598 ReadBarrierOption read_barrier_option) {
6599 Register out_reg = out.AsRegister<Register>();
6600 if (read_barrier_option == kWithReadBarrier) {
6601 CHECK(kEmitCompilerReadBarrier);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006602 if (!kUseBakerReadBarrier || !kBakerReadBarrierThunksEnableForFields) {
6603 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
6604 }
Alexey Frunze15958152017-02-09 19:08:30 -08006605 if (kUseBakerReadBarrier) {
6606 // Load with fast path based Baker's read barrier.
6607 // /* HeapReference<Object> */ out = *(out + offset)
6608 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6609 out,
6610 out_reg,
6611 offset,
6612 maybe_temp,
6613 /* needs_null_check */ false);
6614 } else {
6615 // Load with slow path based read barrier.
6616 // Save the value of `out` into `maybe_temp` before overwriting it
6617 // in the following move operation, as we will need it for the
6618 // read barrier below.
6619 __ Move(maybe_temp.AsRegister<Register>(), out_reg);
6620 // /* HeapReference<Object> */ out = *(out + offset)
6621 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
6622 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
6623 }
6624 } else {
6625 // Plain load with no read barrier.
6626 // /* HeapReference<Object> */ out = *(out + offset)
6627 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
6628 __ MaybeUnpoisonHeapReference(out_reg);
6629 }
6630}
6631
6632void InstructionCodeGeneratorMIPS::GenerateReferenceLoadTwoRegisters(
6633 HInstruction* instruction,
6634 Location out,
6635 Location obj,
6636 uint32_t offset,
6637 Location maybe_temp,
6638 ReadBarrierOption read_barrier_option) {
6639 Register out_reg = out.AsRegister<Register>();
6640 Register obj_reg = obj.AsRegister<Register>();
6641 if (read_barrier_option == kWithReadBarrier) {
6642 CHECK(kEmitCompilerReadBarrier);
6643 if (kUseBakerReadBarrier) {
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006644 if (!kBakerReadBarrierThunksEnableForFields) {
6645 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
6646 }
Alexey Frunze15958152017-02-09 19:08:30 -08006647 // Load with fast path based Baker's read barrier.
6648 // /* HeapReference<Object> */ out = *(obj + offset)
6649 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6650 out,
6651 obj_reg,
6652 offset,
6653 maybe_temp,
6654 /* needs_null_check */ false);
6655 } else {
6656 // Load with slow path based read barrier.
6657 // /* HeapReference<Object> */ out = *(obj + offset)
6658 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
6659 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
6660 }
6661 } else {
6662 // Plain load with no read barrier.
6663 // /* HeapReference<Object> */ out = *(obj + offset)
6664 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
6665 __ MaybeUnpoisonHeapReference(out_reg);
6666 }
6667}
6668
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006669static inline int GetBakerMarkThunkNumber(Register reg) {
6670 static_assert(BAKER_MARK_INTROSPECTION_REGISTER_COUNT == 21, "Expecting equal");
6671 if (reg >= V0 && reg <= T7) { // 14 consequtive regs.
6672 return reg - V0;
6673 } else if (reg >= S2 && reg <= S7) { // 6 consequtive regs.
6674 return 14 + (reg - S2);
6675 } else if (reg == FP) { // One more.
6676 return 20;
6677 }
6678 LOG(FATAL) << "Unexpected register " << reg;
6679 UNREACHABLE();
6680}
6681
6682static inline int GetBakerMarkFieldArrayThunkDisplacement(Register reg, bool short_offset) {
6683 int num = GetBakerMarkThunkNumber(reg) +
6684 (short_offset ? BAKER_MARK_INTROSPECTION_REGISTER_COUNT : 0);
6685 return num * BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE;
6686}
6687
6688static inline int GetBakerMarkGcRootThunkDisplacement(Register reg) {
6689 return GetBakerMarkThunkNumber(reg) * BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE +
6690 BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET;
6691}
6692
Alexey Frunze15958152017-02-09 19:08:30 -08006693void InstructionCodeGeneratorMIPS::GenerateGcRootFieldLoad(HInstruction* instruction,
6694 Location root,
6695 Register obj,
6696 uint32_t offset,
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006697 ReadBarrierOption read_barrier_option,
6698 MipsLabel* label_low) {
6699 bool reordering;
6700 if (label_low != nullptr) {
6701 DCHECK_EQ(offset, 0x5678u);
6702 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07006703 Register root_reg = root.AsRegister<Register>();
Alexey Frunze15958152017-02-09 19:08:30 -08006704 if (read_barrier_option == kWithReadBarrier) {
6705 DCHECK(kEmitCompilerReadBarrier);
6706 if (kUseBakerReadBarrier) {
6707 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
6708 // Baker's read barrier are used:
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006709 if (kBakerReadBarrierThunksEnableForGcRoots) {
6710 // Note that we do not actually check the value of `GetIsGcMarking()`
6711 // to decide whether to mark the loaded GC root or not. Instead, we
6712 // load into `temp` (T9) the read barrier mark introspection entrypoint.
6713 // If `temp` is null, it means that `GetIsGcMarking()` is false, and
6714 // vice versa.
6715 //
6716 // We use thunks for the slow path. That thunk checks the reference
6717 // and jumps to the entrypoint if needed.
6718 //
6719 // temp = Thread::Current()->pReadBarrierMarkReg00
6720 // // AKA &art_quick_read_barrier_mark_introspection.
6721 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
6722 // if (temp != nullptr) {
6723 // temp = &gc_root_thunk<root_reg>
6724 // root = temp(root)
6725 // }
Alexey Frunze15958152017-02-09 19:08:30 -08006726
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006727 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
6728 const int32_t entry_point_offset =
6729 Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(0);
6730 const int thunk_disp = GetBakerMarkGcRootThunkDisplacement(root_reg);
6731 int16_t offset_low = Low16Bits(offset);
6732 int16_t offset_high = High16Bits(offset - offset_low); // Accounts for sign
6733 // extension in lw.
6734 bool short_offset = IsInt<16>(static_cast<int32_t>(offset));
6735 Register base = short_offset ? obj : TMP;
6736 // Loading the entrypoint does not require a load acquire since it is only changed when
6737 // threads are suspended or running a checkpoint.
6738 __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset);
6739 reordering = __ SetReorder(false);
6740 if (!short_offset) {
6741 DCHECK(!label_low);
6742 __ AddUpper(base, obj, offset_high);
6743 }
Alexey Frunze0cab6562017-07-25 15:19:36 -07006744 MipsLabel skip_call;
6745 __ Beqz(T9, &skip_call, /* is_bare */ true);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006746 if (label_low != nullptr) {
6747 DCHECK(short_offset);
6748 __ Bind(label_low);
6749 }
6750 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
6751 __ LoadFromOffset(kLoadWord, root_reg, base, offset_low); // Single instruction
6752 // in delay slot.
6753 if (isR6) {
6754 __ Jialc(T9, thunk_disp);
6755 } else {
6756 __ Addiu(T9, T9, thunk_disp);
6757 __ Jalr(T9);
6758 __ Nop();
6759 }
Alexey Frunze0cab6562017-07-25 15:19:36 -07006760 __ Bind(&skip_call);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006761 __ SetReorder(reordering);
6762 } else {
6763 // Note that we do not actually check the value of `GetIsGcMarking()`
6764 // to decide whether to mark the loaded GC root or not. Instead, we
6765 // load into `temp` (T9) the read barrier mark entry point corresponding
6766 // to register `root`. If `temp` is null, it means that `GetIsGcMarking()`
6767 // is false, and vice versa.
6768 //
6769 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
6770 // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
6771 // if (temp != null) {
6772 // root = temp(root)
6773 // }
Alexey Frunze15958152017-02-09 19:08:30 -08006774
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006775 if (label_low != nullptr) {
6776 reordering = __ SetReorder(false);
6777 __ Bind(label_low);
6778 }
6779 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
6780 __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
6781 if (label_low != nullptr) {
6782 __ SetReorder(reordering);
6783 }
6784 static_assert(
6785 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
6786 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
6787 "have different sizes.");
6788 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
6789 "art::mirror::CompressedReference<mirror::Object> and int32_t "
6790 "have different sizes.");
Alexey Frunze15958152017-02-09 19:08:30 -08006791
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006792 // Slow path marking the GC root `root`.
6793 Location temp = Location::RegisterLocation(T9);
6794 SlowPathCodeMIPS* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006795 new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathMIPS(
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006796 instruction,
6797 root,
6798 /*entrypoint*/ temp);
6799 codegen_->AddSlowPath(slow_path);
6800
6801 const int32_t entry_point_offset =
6802 Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(root.reg() - 1);
6803 // Loading the entrypoint does not require a load acquire since it is only changed when
6804 // threads are suspended or running a checkpoint.
6805 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, entry_point_offset);
6806 __ Bnez(temp.AsRegister<Register>(), slow_path->GetEntryLabel());
6807 __ Bind(slow_path->GetExitLabel());
6808 }
Alexey Frunze15958152017-02-09 19:08:30 -08006809 } else {
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006810 if (label_low != nullptr) {
6811 reordering = __ SetReorder(false);
6812 __ Bind(label_low);
6813 }
Alexey Frunze15958152017-02-09 19:08:30 -08006814 // GC root loaded through a slow path for read barriers other
6815 // than Baker's.
6816 // /* GcRoot<mirror::Object>* */ root = obj + offset
6817 __ Addiu32(root_reg, obj, offset);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006818 if (label_low != nullptr) {
6819 __ SetReorder(reordering);
6820 }
Alexey Frunze15958152017-02-09 19:08:30 -08006821 // /* mirror::Object* */ root = root->Read()
6822 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
6823 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07006824 } else {
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006825 if (label_low != nullptr) {
6826 reordering = __ SetReorder(false);
6827 __ Bind(label_low);
6828 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07006829 // Plain GC root load with no read barrier.
6830 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
6831 __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
6832 // Note that GC roots are not affected by heap poisoning, thus we
6833 // do not have to unpoison `root_reg` here.
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006834 if (label_low != nullptr) {
6835 __ SetReorder(reordering);
6836 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07006837 }
6838}
6839
Alexey Frunze15958152017-02-09 19:08:30 -08006840void CodeGeneratorMIPS::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
6841 Location ref,
6842 Register obj,
6843 uint32_t offset,
6844 Location temp,
6845 bool needs_null_check) {
6846 DCHECK(kEmitCompilerReadBarrier);
6847 DCHECK(kUseBakerReadBarrier);
6848
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006849 if (kBakerReadBarrierThunksEnableForFields) {
6850 // Note that we do not actually check the value of `GetIsGcMarking()`
6851 // to decide whether to mark the loaded reference or not. Instead, we
6852 // load into `temp` (T9) the read barrier mark introspection entrypoint.
6853 // If `temp` is null, it means that `GetIsGcMarking()` is false, and
6854 // vice versa.
6855 //
6856 // We use thunks for the slow path. That thunk checks the reference
6857 // and jumps to the entrypoint if needed. If the holder is not gray,
6858 // it issues a load-load memory barrier and returns to the original
6859 // reference load.
6860 //
6861 // temp = Thread::Current()->pReadBarrierMarkReg00
6862 // // AKA &art_quick_read_barrier_mark_introspection.
6863 // if (temp != nullptr) {
6864 // temp = &field_array_thunk<holder_reg>
6865 // temp()
6866 // }
6867 // not_gray_return_address:
6868 // // If the offset is too large to fit into the lw instruction, we
6869 // // use an adjusted base register (TMP) here. This register
6870 // // receives bits 16 ... 31 of the offset before the thunk invocation
6871 // // and the thunk benefits from it.
6872 // HeapReference<mirror::Object> reference = *(obj+offset); // Original reference load.
6873 // gray_return_address:
6874
6875 DCHECK(temp.IsInvalid());
6876 bool isR6 = GetInstructionSetFeatures().IsR6();
6877 int16_t offset_low = Low16Bits(offset);
6878 int16_t offset_high = High16Bits(offset - offset_low); // Accounts for sign extension in lw.
6879 bool short_offset = IsInt<16>(static_cast<int32_t>(offset));
6880 bool reordering = __ SetReorder(false);
6881 const int32_t entry_point_offset =
6882 Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(0);
6883 // There may have or may have not been a null check if the field offset is smaller than
6884 // the page size.
6885 // There must've been a null check in case it's actually a load from an array.
6886 // We will, however, perform an explicit null check in the thunk as it's easier to
6887 // do it than not.
6888 if (instruction->IsArrayGet()) {
6889 DCHECK(!needs_null_check);
6890 }
6891 const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, short_offset);
6892 // Loading the entrypoint does not require a load acquire since it is only changed when
6893 // threads are suspended or running a checkpoint.
6894 __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset);
6895 Register ref_reg = ref.AsRegister<Register>();
6896 Register base = short_offset ? obj : TMP;
Alexey Frunze0cab6562017-07-25 15:19:36 -07006897 MipsLabel skip_call;
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006898 if (short_offset) {
6899 if (isR6) {
Alexey Frunze0cab6562017-07-25 15:19:36 -07006900 __ Beqzc(T9, &skip_call, /* is_bare */ true);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006901 __ Nop(); // In forbidden slot.
6902 __ Jialc(T9, thunk_disp);
6903 } else {
Alexey Frunze0cab6562017-07-25 15:19:36 -07006904 __ Beqz(T9, &skip_call, /* is_bare */ true);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006905 __ Addiu(T9, T9, thunk_disp); // In delay slot.
6906 __ Jalr(T9);
6907 __ Nop(); // In delay slot.
6908 }
Alexey Frunze0cab6562017-07-25 15:19:36 -07006909 __ Bind(&skip_call);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006910 } else {
6911 if (isR6) {
Alexey Frunze0cab6562017-07-25 15:19:36 -07006912 __ Beqz(T9, &skip_call, /* is_bare */ true);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006913 __ Aui(base, obj, offset_high); // In delay slot.
6914 __ Jialc(T9, thunk_disp);
Alexey Frunze0cab6562017-07-25 15:19:36 -07006915 __ Bind(&skip_call);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006916 } else {
6917 __ Lui(base, offset_high);
Alexey Frunze0cab6562017-07-25 15:19:36 -07006918 __ Beqz(T9, &skip_call, /* is_bare */ true);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006919 __ Addiu(T9, T9, thunk_disp); // In delay slot.
6920 __ Jalr(T9);
Alexey Frunze0cab6562017-07-25 15:19:36 -07006921 __ Bind(&skip_call);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006922 __ Addu(base, base, obj); // In delay slot.
6923 }
6924 }
6925 // /* HeapReference<Object> */ ref = *(obj + offset)
6926 __ LoadFromOffset(kLoadWord, ref_reg, base, offset_low); // Single instruction.
6927 if (needs_null_check) {
6928 MaybeRecordImplicitNullCheck(instruction);
6929 }
6930 __ MaybeUnpoisonHeapReference(ref_reg);
6931 __ SetReorder(reordering);
6932 return;
6933 }
6934
Alexey Frunze15958152017-02-09 19:08:30 -08006935 // /* HeapReference<Object> */ ref = *(obj + offset)
6936 Location no_index = Location::NoLocation();
6937 ScaleFactor no_scale_factor = TIMES_1;
6938 GenerateReferenceLoadWithBakerReadBarrier(instruction,
6939 ref,
6940 obj,
6941 offset,
6942 no_index,
6943 no_scale_factor,
6944 temp,
6945 needs_null_check);
6946}
6947
6948void CodeGeneratorMIPS::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
6949 Location ref,
6950 Register obj,
6951 uint32_t data_offset,
6952 Location index,
6953 Location temp,
6954 bool needs_null_check) {
6955 DCHECK(kEmitCompilerReadBarrier);
6956 DCHECK(kUseBakerReadBarrier);
6957
6958 static_assert(
6959 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
6960 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
Alexey Frunze4147fcc2017-06-17 19:57:27 -07006961 ScaleFactor scale_factor = TIMES_4;
6962
6963 if (kBakerReadBarrierThunksEnableForArrays) {
6964 // Note that we do not actually check the value of `GetIsGcMarking()`
6965 // to decide whether to mark the loaded reference or not. Instead, we
6966 // load into `temp` (T9) the read barrier mark introspection entrypoint.
6967 // If `temp` is null, it means that `GetIsGcMarking()` is false, and
6968 // vice versa.
6969 //
6970 // We use thunks for the slow path. That thunk checks the reference
6971 // and jumps to the entrypoint if needed. If the holder is not gray,
6972 // it issues a load-load memory barrier and returns to the original
6973 // reference load.
6974 //
6975 // temp = Thread::Current()->pReadBarrierMarkReg00
6976 // // AKA &art_quick_read_barrier_mark_introspection.
6977 // if (temp != nullptr) {
6978 // temp = &field_array_thunk<holder_reg>
6979 // temp()
6980 // }
6981 // not_gray_return_address:
6982 // // The element address is pre-calculated in the TMP register before the
6983 // // thunk invocation and the thunk benefits from it.
6984 // HeapReference<mirror::Object> reference = data[index]; // Original reference load.
6985 // gray_return_address:
6986
6987 DCHECK(temp.IsInvalid());
6988 DCHECK(index.IsValid());
6989 bool reordering = __ SetReorder(false);
6990 const int32_t entry_point_offset =
6991 Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(0);
6992 // We will not do the explicit null check in the thunk as some form of a null check
6993 // must've been done earlier.
6994 DCHECK(!needs_null_check);
6995 const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, /* short_offset */ false);
6996 // Loading the entrypoint does not require a load acquire since it is only changed when
6997 // threads are suspended or running a checkpoint.
6998 __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset);
6999 Register ref_reg = ref.AsRegister<Register>();
7000 Register index_reg = index.IsRegisterPair()
7001 ? index.AsRegisterPairLow<Register>()
7002 : index.AsRegister<Register>();
Alexey Frunze0cab6562017-07-25 15:19:36 -07007003 MipsLabel skip_call;
Alexey Frunze4147fcc2017-06-17 19:57:27 -07007004 if (GetInstructionSetFeatures().IsR6()) {
Alexey Frunze0cab6562017-07-25 15:19:36 -07007005 __ Beqz(T9, &skip_call, /* is_bare */ true);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07007006 __ Lsa(TMP, index_reg, obj, scale_factor); // In delay slot.
7007 __ Jialc(T9, thunk_disp);
Alexey Frunze0cab6562017-07-25 15:19:36 -07007008 __ Bind(&skip_call);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07007009 } else {
7010 __ Sll(TMP, index_reg, scale_factor);
Alexey Frunze0cab6562017-07-25 15:19:36 -07007011 __ Beqz(T9, &skip_call, /* is_bare */ true);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07007012 __ Addiu(T9, T9, thunk_disp); // In delay slot.
7013 __ Jalr(T9);
Alexey Frunze0cab6562017-07-25 15:19:36 -07007014 __ Bind(&skip_call);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07007015 __ Addu(TMP, TMP, obj); // In delay slot.
7016 }
7017 // /* HeapReference<Object> */ ref = *(obj + data_offset + (index << scale_factor))
7018 DCHECK(IsInt<16>(static_cast<int32_t>(data_offset))) << data_offset;
7019 __ LoadFromOffset(kLoadWord, ref_reg, TMP, data_offset); // Single instruction.
7020 __ MaybeUnpoisonHeapReference(ref_reg);
7021 __ SetReorder(reordering);
7022 return;
7023 }
7024
Alexey Frunze15958152017-02-09 19:08:30 -08007025 // /* HeapReference<Object> */ ref =
7026 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
Alexey Frunze15958152017-02-09 19:08:30 -08007027 GenerateReferenceLoadWithBakerReadBarrier(instruction,
7028 ref,
7029 obj,
7030 data_offset,
7031 index,
7032 scale_factor,
7033 temp,
7034 needs_null_check);
7035}
7036
7037void CodeGeneratorMIPS::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
7038 Location ref,
7039 Register obj,
7040 uint32_t offset,
7041 Location index,
7042 ScaleFactor scale_factor,
7043 Location temp,
7044 bool needs_null_check,
7045 bool always_update_field) {
7046 DCHECK(kEmitCompilerReadBarrier);
7047 DCHECK(kUseBakerReadBarrier);
7048
7049 // In slow path based read barriers, the read barrier call is
7050 // inserted after the original load. However, in fast path based
7051 // Baker's read barriers, we need to perform the load of
7052 // mirror::Object::monitor_ *before* the original reference load.
7053 // This load-load ordering is required by the read barrier.
7054 // The fast path/slow path (for Baker's algorithm) should look like:
7055 //
7056 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
7057 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
7058 // HeapReference<Object> ref = *src; // Original reference load.
7059 // bool is_gray = (rb_state == ReadBarrier::GrayState());
7060 // if (is_gray) {
7061 // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path.
7062 // }
7063 //
7064 // Note: the original implementation in ReadBarrier::Barrier is
7065 // slightly more complex as it performs additional checks that we do
7066 // not do here for performance reasons.
7067
7068 Register ref_reg = ref.AsRegister<Register>();
7069 Register temp_reg = temp.AsRegister<Register>();
7070 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
7071
7072 // /* int32_t */ monitor = obj->monitor_
7073 __ LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset);
7074 if (needs_null_check) {
7075 MaybeRecordImplicitNullCheck(instruction);
7076 }
7077 // /* LockWord */ lock_word = LockWord(monitor)
7078 static_assert(sizeof(LockWord) == sizeof(int32_t),
7079 "art::LockWord and int32_t have different sizes.");
7080
7081 __ Sync(0); // Barrier to prevent load-load reordering.
7082
7083 // The actual reference load.
7084 if (index.IsValid()) {
7085 // Load types involving an "index": ArrayGet,
7086 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
7087 // intrinsics.
7088 // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
7089 if (index.IsConstant()) {
7090 size_t computed_offset =
7091 (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset;
7092 __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
7093 } else {
7094 // Handle the special case of the
7095 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
7096 // intrinsics, which use a register pair as index ("long
7097 // offset"), of which only the low part contains data.
7098 Register index_reg = index.IsRegisterPair()
7099 ? index.AsRegisterPairLow<Register>()
7100 : index.AsRegister<Register>();
Chris Larsencd0295d2017-03-31 15:26:54 -07007101 __ ShiftAndAdd(TMP, index_reg, obj, scale_factor, TMP);
Alexey Frunze15958152017-02-09 19:08:30 -08007102 __ LoadFromOffset(kLoadWord, ref_reg, TMP, offset);
7103 }
7104 } else {
7105 // /* HeapReference<Object> */ ref = *(obj + offset)
7106 __ LoadFromOffset(kLoadWord, ref_reg, obj, offset);
7107 }
7108
7109 // Object* ref = ref_addr->AsMirrorPtr()
7110 __ MaybeUnpoisonHeapReference(ref_reg);
7111
7112 // Slow path marking the object `ref` when it is gray.
7113 SlowPathCodeMIPS* slow_path;
7114 if (always_update_field) {
7115 // ReadBarrierMarkAndUpdateFieldSlowPathMIPS only supports address
7116 // of the form `obj + field_offset`, where `obj` is a register and
7117 // `field_offset` is a register pair (of which only the lower half
7118 // is used). Thus `offset` and `scale_factor` above are expected
7119 // to be null in this code path.
7120 DCHECK_EQ(offset, 0u);
7121 DCHECK_EQ(scale_factor, ScaleFactor::TIMES_1);
Vladimir Marko174b2e22017-10-12 13:34:49 +01007122 slow_path = new (GetScopedAllocator())
Alexey Frunze15958152017-02-09 19:08:30 -08007123 ReadBarrierMarkAndUpdateFieldSlowPathMIPS(instruction,
7124 ref,
7125 obj,
7126 /* field_offset */ index,
7127 temp_reg);
7128 } else {
Vladimir Marko174b2e22017-10-12 13:34:49 +01007129 slow_path = new (GetScopedAllocator()) ReadBarrierMarkSlowPathMIPS(instruction, ref);
Alexey Frunze15958152017-02-09 19:08:30 -08007130 }
7131 AddSlowPath(slow_path);
7132
7133 // if (rb_state == ReadBarrier::GrayState())
7134 // ref = ReadBarrier::Mark(ref);
7135 // Given the numeric representation, it's enough to check the low bit of the
7136 // rb_state. We do that by shifting the bit into the sign bit (31) and
7137 // performing a branch on less than zero.
7138 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
7139 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
7140 static_assert(LockWord::kReadBarrierStateSize == 1, "Expecting 1-bit read barrier state size");
7141 __ Sll(temp_reg, temp_reg, 31 - LockWord::kReadBarrierStateShift);
7142 __ Bltz(temp_reg, slow_path->GetEntryLabel());
7143 __ Bind(slow_path->GetExitLabel());
7144}
7145
7146void CodeGeneratorMIPS::GenerateReadBarrierSlow(HInstruction* instruction,
7147 Location out,
7148 Location ref,
7149 Location obj,
7150 uint32_t offset,
7151 Location index) {
7152 DCHECK(kEmitCompilerReadBarrier);
7153
7154 // Insert a slow path based read barrier *after* the reference load.
7155 //
7156 // If heap poisoning is enabled, the unpoisoning of the loaded
7157 // reference will be carried out by the runtime within the slow
7158 // path.
7159 //
7160 // Note that `ref` currently does not get unpoisoned (when heap
7161 // poisoning is enabled), which is alright as the `ref` argument is
7162 // not used by the artReadBarrierSlow entry point.
7163 //
7164 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
Vladimir Marko174b2e22017-10-12 13:34:49 +01007165 SlowPathCodeMIPS* slow_path = new (GetScopedAllocator())
Alexey Frunze15958152017-02-09 19:08:30 -08007166 ReadBarrierForHeapReferenceSlowPathMIPS(instruction, out, ref, obj, offset, index);
7167 AddSlowPath(slow_path);
7168
7169 __ B(slow_path->GetEntryLabel());
7170 __ Bind(slow_path->GetExitLabel());
7171}
7172
7173void CodeGeneratorMIPS::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
7174 Location out,
7175 Location ref,
7176 Location obj,
7177 uint32_t offset,
7178 Location index) {
7179 if (kEmitCompilerReadBarrier) {
7180 // Baker's read barriers shall be handled by the fast path
7181 // (CodeGeneratorMIPS::GenerateReferenceLoadWithBakerReadBarrier).
7182 DCHECK(!kUseBakerReadBarrier);
7183 // If heap poisoning is enabled, unpoisoning will be taken care of
7184 // by the runtime within the slow path.
7185 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
7186 } else if (kPoisonHeapReferences) {
7187 __ UnpoisonHeapReference(out.AsRegister<Register>());
7188 }
7189}
7190
7191void CodeGeneratorMIPS::GenerateReadBarrierForRootSlow(HInstruction* instruction,
7192 Location out,
7193 Location root) {
7194 DCHECK(kEmitCompilerReadBarrier);
7195
7196 // Insert a slow path based read barrier *after* the GC root load.
7197 //
7198 // Note that GC roots are not affected by heap poisoning, so we do
7199 // not need to do anything special for this here.
7200 SlowPathCodeMIPS* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007201 new (GetScopedAllocator()) ReadBarrierForRootSlowPathMIPS(instruction, out, root);
Alexey Frunze15958152017-02-09 19:08:30 -08007202 AddSlowPath(slow_path);
7203
7204 __ B(slow_path->GetEntryLabel());
7205 __ Bind(slow_path->GetExitLabel());
7206}
7207
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007208void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) {
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007209 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7210 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Alexey Frunzec61c0762017-04-10 13:54:23 -07007211 bool baker_read_barrier_slow_path = false;
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007212 switch (type_check_kind) {
7213 case TypeCheckKind::kExactCheck:
7214 case TypeCheckKind::kAbstractClassCheck:
7215 case TypeCheckKind::kClassHierarchyCheck:
7216 case TypeCheckKind::kArrayObjectCheck:
Alexey Frunze15958152017-02-09 19:08:30 -08007217 call_kind =
7218 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
Alexey Frunzec61c0762017-04-10 13:54:23 -07007219 baker_read_barrier_slow_path = kUseBakerReadBarrier;
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007220 break;
7221 case TypeCheckKind::kArrayCheck:
7222 case TypeCheckKind::kUnresolvedCheck:
7223 case TypeCheckKind::kInterfaceCheck:
7224 call_kind = LocationSummary::kCallOnSlowPath;
7225 break;
7226 }
7227
Vladimir Markoca6fff82017-10-03 14:49:14 +01007228 LocationSummary* locations =
7229 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Alexey Frunzec61c0762017-04-10 13:54:23 -07007230 if (baker_read_barrier_slow_path) {
7231 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
7232 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007233 locations->SetInAt(0, Location::RequiresRegister());
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00007234 locations->SetInAt(1, Location::RequiresRegister());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007235 // The output does overlap inputs.
7236 // Note that TypeCheckSlowPathMIPS uses this register too.
7237 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Alexey Frunze15958152017-02-09 19:08:30 -08007238 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007239}
7240
7241void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007242 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007243 LocationSummary* locations = instruction->GetLocations();
Alexey Frunze15958152017-02-09 19:08:30 -08007244 Location obj_loc = locations->InAt(0);
7245 Register obj = obj_loc.AsRegister<Register>();
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00007246 Register cls = locations->InAt(1).AsRegister<Register>();
Alexey Frunze15958152017-02-09 19:08:30 -08007247 Location out_loc = locations->Out();
7248 Register out = out_loc.AsRegister<Register>();
7249 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7250 DCHECK_LE(num_temps, 1u);
7251 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007252 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7253 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7254 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7255 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007256 MipsLabel done;
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007257 SlowPathCodeMIPS* slow_path = nullptr;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007258
7259 // Return 0 if `obj` is null.
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007260 // Avoid this check if we know `obj` is not null.
7261 if (instruction->MustDoNullCheck()) {
7262 __ Move(out, ZERO);
7263 __ Beqz(obj, &done);
7264 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007265
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007266 switch (type_check_kind) {
7267 case TypeCheckKind::kExactCheck: {
7268 // /* HeapReference<Class> */ out = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08007269 GenerateReferenceLoadTwoRegisters(instruction,
7270 out_loc,
7271 obj_loc,
7272 class_offset,
7273 maybe_temp_loc,
7274 kCompilerReadBarrierOption);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007275 // Classes must be equal for the instanceof to succeed.
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00007276 __ Xor(out, out, cls);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007277 __ Sltiu(out, out, 1);
7278 break;
7279 }
7280
7281 case TypeCheckKind::kAbstractClassCheck: {
7282 // /* HeapReference<Class> */ out = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08007283 GenerateReferenceLoadTwoRegisters(instruction,
7284 out_loc,
7285 obj_loc,
7286 class_offset,
7287 maybe_temp_loc,
7288 kCompilerReadBarrierOption);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007289 // If the class is abstract, we eagerly fetch the super class of the
7290 // object to avoid doing a comparison we know will fail.
7291 MipsLabel loop;
7292 __ Bind(&loop);
7293 // /* HeapReference<Class> */ out = out->super_class_
Alexey Frunze15958152017-02-09 19:08:30 -08007294 GenerateReferenceLoadOneRegister(instruction,
7295 out_loc,
7296 super_offset,
7297 maybe_temp_loc,
7298 kCompilerReadBarrierOption);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007299 // If `out` is null, we use it for the result, and jump to `done`.
7300 __ Beqz(out, &done);
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00007301 __ Bne(out, cls, &loop);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007302 __ LoadConst32(out, 1);
7303 break;
7304 }
7305
7306 case TypeCheckKind::kClassHierarchyCheck: {
7307 // /* HeapReference<Class> */ out = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08007308 GenerateReferenceLoadTwoRegisters(instruction,
7309 out_loc,
7310 obj_loc,
7311 class_offset,
7312 maybe_temp_loc,
7313 kCompilerReadBarrierOption);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007314 // Walk over the class hierarchy to find a match.
7315 MipsLabel loop, success;
7316 __ Bind(&loop);
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00007317 __ Beq(out, cls, &success);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007318 // /* HeapReference<Class> */ out = out->super_class_
Alexey Frunze15958152017-02-09 19:08:30 -08007319 GenerateReferenceLoadOneRegister(instruction,
7320 out_loc,
7321 super_offset,
7322 maybe_temp_loc,
7323 kCompilerReadBarrierOption);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007324 __ Bnez(out, &loop);
7325 // If `out` is null, we use it for the result, and jump to `done`.
7326 __ B(&done);
7327 __ Bind(&success);
7328 __ LoadConst32(out, 1);
7329 break;
7330 }
7331
7332 case TypeCheckKind::kArrayObjectCheck: {
7333 // /* HeapReference<Class> */ out = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08007334 GenerateReferenceLoadTwoRegisters(instruction,
7335 out_loc,
7336 obj_loc,
7337 class_offset,
7338 maybe_temp_loc,
7339 kCompilerReadBarrierOption);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007340 // Do an exact check.
7341 MipsLabel success;
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00007342 __ Beq(out, cls, &success);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007343 // Otherwise, we need to check that the object's class is a non-primitive array.
7344 // /* HeapReference<Class> */ out = out->component_type_
Alexey Frunze15958152017-02-09 19:08:30 -08007345 GenerateReferenceLoadOneRegister(instruction,
7346 out_loc,
7347 component_offset,
7348 maybe_temp_loc,
7349 kCompilerReadBarrierOption);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007350 // If `out` is null, we use it for the result, and jump to `done`.
7351 __ Beqz(out, &done);
7352 __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
7353 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
7354 __ Sltiu(out, out, 1);
7355 __ B(&done);
7356 __ Bind(&success);
7357 __ LoadConst32(out, 1);
7358 break;
7359 }
7360
7361 case TypeCheckKind::kArrayCheck: {
7362 // No read barrier since the slow path will retry upon failure.
7363 // /* HeapReference<Class> */ out = obj->klass_
Alexey Frunze15958152017-02-09 19:08:30 -08007364 GenerateReferenceLoadTwoRegisters(instruction,
7365 out_loc,
7366 obj_loc,
7367 class_offset,
7368 maybe_temp_loc,
7369 kWithoutReadBarrier);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007370 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007371 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(
7372 instruction, /* is_fatal */ false);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007373 codegen_->AddSlowPath(slow_path);
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00007374 __ Bne(out, cls, slow_path->GetEntryLabel());
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007375 __ LoadConst32(out, 1);
7376 break;
7377 }
7378
7379 case TypeCheckKind::kUnresolvedCheck:
7380 case TypeCheckKind::kInterfaceCheck: {
7381 // Note that we indeed only call on slow path, but we always go
7382 // into the slow path for the unresolved and interface check
7383 // cases.
7384 //
7385 // We cannot directly call the InstanceofNonTrivial runtime
7386 // entry point without resorting to a type checking slow path
7387 // here (i.e. by calling InvokeRuntime directly), as it would
7388 // require to assign fixed registers for the inputs of this
7389 // HInstanceOf instruction (following the runtime calling
7390 // convention), which might be cluttered by the potential first
7391 // read barrier emission at the beginning of this method.
7392 //
7393 // TODO: Introduce a new runtime entry point taking the object
7394 // to test (instead of its class) as argument, and let it deal
7395 // with the read barrier issues. This will let us refactor this
7396 // case of the `switch` code as it was previously (with a direct
7397 // call to the runtime not using a type checking slow path).
7398 // This should also be beneficial for the other cases above.
7399 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007400 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(
7401 instruction, /* is_fatal */ false);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007402 codegen_->AddSlowPath(slow_path);
7403 __ B(slow_path->GetEntryLabel());
7404 break;
7405 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007406 }
7407
7408 __ Bind(&done);
Alexey Frunze66b69ad2017-02-24 00:51:44 -08007409
7410 if (slow_path != nullptr) {
7411 __ Bind(slow_path->GetExitLabel());
7412 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007413}
7414
7415void LocationsBuilderMIPS::VisitIntConstant(HIntConstant* constant) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007416 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007417 locations->SetOut(Location::ConstantLocation(constant));
7418}
7419
7420void InstructionCodeGeneratorMIPS::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
7421 // Will be generated at use site.
7422}
7423
7424void LocationsBuilderMIPS::VisitNullConstant(HNullConstant* constant) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007425 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007426 locations->SetOut(Location::ConstantLocation(constant));
7427}
7428
7429void InstructionCodeGeneratorMIPS::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
7430 // Will be generated at use site.
7431}
7432
7433void LocationsBuilderMIPS::HandleInvoke(HInvoke* invoke) {
7434 InvokeDexCallingConventionVisitorMIPS calling_convention_visitor;
7435 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
7436}
7437
7438void LocationsBuilderMIPS::VisitInvokeInterface(HInvokeInterface* invoke) {
7439 HandleInvoke(invoke);
Alexey Frunze1b8464d2016-11-12 17:22:05 -08007440 // The register T7 is required to be used for the hidden argument in
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007441 // art_quick_imt_conflict_trampoline, so add the hidden argument.
Alexey Frunze1b8464d2016-11-12 17:22:05 -08007442 invoke->GetLocations()->AddTemp(Location::RegisterLocation(T7));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007443}
7444
7445void InstructionCodeGeneratorMIPS::VisitInvokeInterface(HInvokeInterface* invoke) {
7446 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
7447 Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007448 Location receiver = invoke->GetLocations()->InAt(0);
7449 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Andreas Gampe542451c2016-07-26 09:02:02 -07007450 Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMipsPointerSize);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007451
7452 // Set the hidden argument.
7453 __ LoadConst32(invoke->GetLocations()->GetTemp(1).AsRegister<Register>(),
7454 invoke->GetDexMethodIndex());
7455
7456 // temp = object->GetClass();
7457 if (receiver.IsStackSlot()) {
7458 __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex());
7459 __ LoadFromOffset(kLoadWord, temp, temp, class_offset);
7460 } else {
7461 __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
7462 }
7463 codegen_->MaybeRecordImplicitNullCheck(invoke);
Alexey Frunzec061de12017-02-14 13:27:23 -08007464 // Instead of simply (possibly) unpoisoning `temp` here, we should
7465 // emit a read barrier for the previous class reference load.
7466 // However this is not required in practice, as this is an
7467 // intermediate/temporary reference and because the current
7468 // concurrent copying collector keeps the from-space memory
7469 // intact/accessible until the end of the marking phase (the
7470 // concurrent copying collector may not in the future).
7471 __ MaybeUnpoisonHeapReference(temp);
Artem Udovichenkoa62cb9b2016-06-30 09:18:25 +00007472 __ LoadFromOffset(kLoadWord, temp, temp,
7473 mirror::Class::ImtPtrOffset(kMipsPointerSize).Uint32Value());
7474 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
Matthew Gharrity465ecc82016-07-19 21:32:52 +00007475 invoke->GetImtIndex(), kMipsPointerSize));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007476 // temp = temp->GetImtEntryAt(method_offset);
7477 __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
7478 // T9 = temp->GetEntryPoint();
7479 __ LoadFromOffset(kLoadWord, T9, temp, entry_point.Int32Value());
7480 // T9();
7481 __ Jalr(T9);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07007482 __ NopIfNoReordering();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007483 DCHECK(!codegen_->IsLeafMethod());
7484 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
7485}
7486
7487void LocationsBuilderMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Chris Larsen701566a2015-10-27 15:29:13 -07007488 IntrinsicLocationsBuilderMIPS intrinsic(codegen_);
7489 if (intrinsic.TryDispatch(invoke)) {
7490 return;
7491 }
7492
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007493 HandleInvoke(invoke);
7494}
7495
7496void LocationsBuilderMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
David Brazdil58282f42016-01-14 12:45:10 +00007497 // Explicit clinit checks triggered by static invokes must have been pruned by
7498 // art::PrepareForRegisterAllocation.
7499 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007500
Alexey Frunze6b892cd2017-01-03 17:11:38 -08007501 bool is_r6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007502 bool has_irreducible_loops = codegen_->GetGraph()->HasIrreducibleLoops();
7503 bool has_extra_input = invoke->HasPcRelativeMethodLoadKind() && !is_r6 && !has_irreducible_loops;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007504
Chris Larsen701566a2015-10-27 15:29:13 -07007505 IntrinsicLocationsBuilderMIPS intrinsic(codegen_);
7506 if (intrinsic.TryDispatch(invoke)) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007507 if (invoke->GetLocations()->CanCall() && has_extra_input) {
7508 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any());
7509 }
Chris Larsen701566a2015-10-27 15:29:13 -07007510 return;
7511 }
7512
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007513 HandleInvoke(invoke);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007514
7515 // Add the extra input register if either the dex cache array base register
7516 // or the PC-relative base register for accessing literals is needed.
7517 if (has_extra_input) {
7518 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::RequiresRegister());
7519 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007520}
7521
Orion Hodsonac141392017-01-13 11:53:47 +00007522void LocationsBuilderMIPS::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
7523 HandleInvoke(invoke);
7524}
7525
7526void InstructionCodeGeneratorMIPS::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
7527 codegen_->GenerateInvokePolymorphicCall(invoke);
7528}
7529
Chris Larsen701566a2015-10-27 15:29:13 -07007530static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007531 if (invoke->GetLocations()->Intrinsified()) {
Chris Larsen701566a2015-10-27 15:29:13 -07007532 IntrinsicCodeGeneratorMIPS intrinsic(codegen);
7533 intrinsic.Dispatch(invoke);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007534 return true;
7535 }
7536 return false;
7537}
7538
Vladimir Markocac5a7e2016-02-22 10:39:50 +00007539HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind(
Alexey Frunze06a46c42016-07-19 15:00:40 -07007540 HLoadString::LoadKind desired_string_load_kind) {
Alexey Frunze06a46c42016-07-19 15:00:40 -07007541 switch (desired_string_load_kind) {
Alexey Frunze06a46c42016-07-19 15:00:40 -07007542 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007543 case HLoadString::LoadKind::kBootImageInternTable:
Vladimir Markoaad75c62016-10-03 08:46:48 +00007544 case HLoadString::LoadKind::kBssEntry:
Alexey Frunze06a46c42016-07-19 15:00:40 -07007545 DCHECK(!Runtime::Current()->UseJitCompilation());
Alexey Frunze06a46c42016-07-19 15:00:40 -07007546 break;
Nicolas Geoffray132d8362016-11-16 09:19:42 +00007547 case HLoadString::LoadKind::kJitTableAddress:
7548 DCHECK(Runtime::Current()->UseJitCompilation());
Nicolas Geoffray132d8362016-11-16 09:19:42 +00007549 break;
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007550 case HLoadString::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007551 case HLoadString::LoadKind::kRuntimeCall:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007552 break;
Alexey Frunze06a46c42016-07-19 15:00:40 -07007553 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07007554 return desired_string_load_kind;
Vladimir Markocac5a7e2016-02-22 10:39:50 +00007555}
7556
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01007557HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind(
7558 HLoadClass::LoadKind desired_class_load_kind) {
Alexey Frunze06a46c42016-07-19 15:00:40 -07007559 switch (desired_class_load_kind) {
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007560 case HLoadClass::LoadKind::kInvalid:
7561 LOG(FATAL) << "UNREACHABLE";
7562 UNREACHABLE();
Alexey Frunze06a46c42016-07-19 15:00:40 -07007563 case HLoadClass::LoadKind::kReferrersClass:
Alexey Frunze06a46c42016-07-19 15:00:40 -07007564 break;
Alexey Frunze06a46c42016-07-19 15:00:40 -07007565 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007566 case HLoadClass::LoadKind::kBootImageClassTable:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007567 case HLoadClass::LoadKind::kBssEntry:
7568 DCHECK(!Runtime::Current()->UseJitCompilation());
7569 break;
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007570 case HLoadClass::LoadKind::kJitTableAddress:
Alexey Frunze06a46c42016-07-19 15:00:40 -07007571 DCHECK(Runtime::Current()->UseJitCompilation());
Alexey Frunze06a46c42016-07-19 15:00:40 -07007572 break;
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007573 case HLoadClass::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007574 case HLoadClass::LoadKind::kRuntimeCall:
Alexey Frunze06a46c42016-07-19 15:00:40 -07007575 break;
7576 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07007577 return desired_class_load_kind;
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01007578}
7579
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007580Register CodeGeneratorMIPS::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
7581 Register temp) {
Alexey Frunze6b892cd2017-01-03 17:11:38 -08007582 CHECK(!GetInstructionSetFeatures().IsR6());
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007583 CHECK(!GetGraph()->HasIrreducibleLoops());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007584 CHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
7585 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
7586 if (!invoke->GetLocations()->Intrinsified()) {
7587 return location.AsRegister<Register>();
7588 }
7589 // For intrinsics we allow any location, so it may be on the stack.
7590 if (!location.IsRegister()) {
7591 __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex());
7592 return temp;
7593 }
7594 // For register locations, check if the register was saved. If so, get it from the stack.
7595 // Note: There is a chance that the register was saved but not overwritten, so we could
7596 // save one load. However, since this is just an intrinsic slow path we prefer this
7597 // simple and more robust approach rather that trying to determine if that's the case.
7598 SlowPathCode* slow_path = GetCurrentSlowPath();
7599 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path.
7600 if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) {
7601 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>());
7602 __ LoadFromOffset(kLoadWord, temp, SP, stack_offset);
7603 return temp;
7604 }
7605 return location.AsRegister<Register>();
7606}
7607
Vladimir Markodc151b22015-10-15 18:02:30 +01007608HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch(
7609 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
Nicolas Geoffray5e4e11e2016-09-22 13:17:41 +01007610 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007611 return desired_dispatch_info;
Vladimir Markodc151b22015-10-15 18:02:30 +01007612}
7613
Vladimir Markoe7197bf2017-06-02 17:00:23 +01007614void CodeGeneratorMIPS::GenerateStaticOrDirectCall(
7615 HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007616 // All registers are assumed to be correctly set up per the calling convention.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007617 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007618 HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
7619 HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
Alexey Frunze6b892cd2017-01-03 17:11:38 -08007620 bool is_r6 = GetInstructionSetFeatures().IsR6();
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007621 bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
7622 Register base_reg = (invoke->HasPcRelativeMethodLoadKind() && !is_r6 && !has_irreducible_loops)
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007623 ? GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister<Register>())
7624 : ZERO;
7625
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007626 switch (method_load_kind) {
Nicolas Geoffrayda079bb2016-09-26 17:56:07 +01007627 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007628 // temp = thread->string_init_entrypoint
Nicolas Geoffrayda079bb2016-09-26 17:56:07 +01007629 uint32_t offset =
7630 GetThreadOffset<kMipsPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007631 __ LoadFromOffset(kLoadWord,
7632 temp.AsRegister<Register>(),
7633 TR,
Nicolas Geoffrayda079bb2016-09-26 17:56:07 +01007634 offset);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007635 break;
Nicolas Geoffrayda079bb2016-09-26 17:56:07 +01007636 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007637 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
Vladimir Markoc53c0792015-11-19 15:48:33 +00007638 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007639 break;
Vladimir Marko65979462017-05-19 17:25:12 +01007640 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
7641 DCHECK(GetCompilerOptions().IsBootImage());
Alexey Frunze5fa5c042017-06-01 21:07:52 -07007642 PcRelativePatchInfo* info_high = NewPcRelativeMethodPatch(invoke->GetTargetMethod());
7643 PcRelativePatchInfo* info_low =
7644 NewPcRelativeMethodPatch(invoke->GetTargetMethod(), info_high);
Vladimir Marko65979462017-05-19 17:25:12 +01007645 Register temp_reg = temp.AsRegister<Register>();
Alexey Frunzea663d9d2017-07-31 18:43:18 -07007646 EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg);
7647 __ Addiu(temp_reg, TMP, /* placeholder */ 0x5678, &info_low->label);
Vladimir Marko65979462017-05-19 17:25:12 +01007648 break;
7649 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007650 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
7651 __ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress());
7652 break;
Vladimir Marko0eb882b2017-05-15 13:39:18 +01007653 case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
Alexey Frunze5fa5c042017-06-01 21:07:52 -07007654 PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(
Vladimir Marko0eb882b2017-05-15 13:39:18 +01007655 MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
Alexey Frunze5fa5c042017-06-01 21:07:52 -07007656 PcRelativePatchInfo* info_low = NewMethodBssEntryPatch(
7657 MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()), info_high);
Vladimir Marko0eb882b2017-05-15 13:39:18 +01007658 Register temp_reg = temp.AsRegister<Register>();
Alexey Frunzea663d9d2017-07-31 18:43:18 -07007659 EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg);
7660 __ Lw(temp_reg, TMP, /* placeholder */ 0x5678, &info_low->label);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007661 break;
Vladimir Marko0eb882b2017-05-15 13:39:18 +01007662 }
Vladimir Markoe7197bf2017-06-02 17:00:23 +01007663 case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
7664 GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
7665 return; // No code pointer retrieval; the runtime performs the call directly.
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007666 }
7667 }
7668
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007669 switch (code_ptr_location) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007670 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07007671 __ Bal(&frame_entry_label_);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007672 break;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007673 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
7674 // T9 = callee_method->entry_point_from_quick_compiled_code_;
Goran Jakovljevic1a878372015-10-26 14:28:52 +01007675 __ LoadFromOffset(kLoadWord,
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007676 T9,
7677 callee_method.AsRegister<Register>(),
7678 ArtMethod::EntryPointFromQuickCompiledCodeOffset(
Andreas Gampe542451c2016-07-26 09:02:02 -07007679 kMipsPointerSize).Int32Value());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007680 // T9()
7681 __ Jalr(T9);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07007682 __ NopIfNoReordering();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007683 break;
7684 }
Vladimir Markoe7197bf2017-06-02 17:00:23 +01007685 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
7686
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007687 DCHECK(!IsLeafMethod());
7688}
7689
7690void InstructionCodeGeneratorMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
David Brazdil58282f42016-01-14 12:45:10 +00007691 // Explicit clinit checks triggered by static invokes must have been pruned by
7692 // art::PrepareForRegisterAllocation.
7693 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007694
7695 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
7696 return;
7697 }
7698
7699 LocationSummary* locations = invoke->GetLocations();
7700 codegen_->GenerateStaticOrDirectCall(invoke,
7701 locations->HasTemps()
7702 ? locations->GetTemp(0)
7703 : Location::NoLocation());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007704}
7705
Vladimir Markoe7197bf2017-06-02 17:00:23 +01007706void CodeGeneratorMIPS::GenerateVirtualCall(
7707 HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
Goran Jakovljevice919b072016-10-04 10:17:34 +02007708 // Use the calling convention instead of the location of the receiver, as
7709 // intrinsics may have put the receiver in a different register. In the intrinsics
7710 // slow path, the arguments have been moved to the right place, so here we are
7711 // guaranteed that the receiver is the first register of the calling convention.
7712 InvokeDexCallingConvention calling_convention;
7713 Register receiver = calling_convention.GetRegisterAt(0);
7714
Chris Larsen3acee732015-11-18 13:31:08 -08007715 Register temp = temp_location.AsRegister<Register>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007716 size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
7717 invoke->GetVTableIndex(), kMipsPointerSize).SizeValue();
7718 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Andreas Gampe542451c2016-07-26 09:02:02 -07007719 Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMipsPointerSize);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007720
7721 // temp = object->GetClass();
Goran Jakovljevice919b072016-10-04 10:17:34 +02007722 __ LoadFromOffset(kLoadWord, temp, receiver, class_offset);
Chris Larsen3acee732015-11-18 13:31:08 -08007723 MaybeRecordImplicitNullCheck(invoke);
Alexey Frunzec061de12017-02-14 13:27:23 -08007724 // Instead of simply (possibly) unpoisoning `temp` here, we should
7725 // emit a read barrier for the previous class reference load.
7726 // However this is not required in practice, as this is an
7727 // intermediate/temporary reference and because the current
7728 // concurrent copying collector keeps the from-space memory
7729 // intact/accessible until the end of the marking phase (the
7730 // concurrent copying collector may not in the future).
7731 __ MaybeUnpoisonHeapReference(temp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007732 // temp = temp->GetMethodAt(method_offset);
7733 __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
7734 // T9 = temp->GetEntryPoint();
7735 __ LoadFromOffset(kLoadWord, T9, temp, entry_point.Int32Value());
7736 // T9();
7737 __ Jalr(T9);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07007738 __ NopIfNoReordering();
Vladimir Markoe7197bf2017-06-02 17:00:23 +01007739 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
Chris Larsen3acee732015-11-18 13:31:08 -08007740}
7741
7742void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
7743 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
7744 return;
7745 }
7746
7747 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007748 DCHECK(!codegen_->IsLeafMethod());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007749}
7750
7751void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) {
Vladimir Marko41559982017-01-06 14:04:23 +00007752 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007753 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Alexey Frunze06a46c42016-07-19 15:00:40 -07007754 InvokeRuntimeCallingConvention calling_convention;
Alexey Frunzec61c0762017-04-10 13:54:23 -07007755 Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
7756 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(cls, loc, loc);
Alexey Frunze06a46c42016-07-19 15:00:40 -07007757 return;
7758 }
Vladimir Marko41559982017-01-06 14:04:23 +00007759 DCHECK(!cls->NeedsAccessCheck());
Alexey Frunzec61c0762017-04-10 13:54:23 -07007760 const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007761 const bool has_irreducible_loops = codegen_->GetGraph()->HasIrreducibleLoops();
Alexey Frunze15958152017-02-09 19:08:30 -08007762 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
7763 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Alexey Frunze06a46c42016-07-19 15:00:40 -07007764 ? LocationSummary::kCallOnSlowPath
7765 : LocationSummary::kNoCall;
Vladimir Markoca6fff82017-10-03 14:49:14 +01007766 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
Alexey Frunzec61c0762017-04-10 13:54:23 -07007767 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
7768 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
7769 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07007770 switch (load_kind) {
7771 // We need an extra register for PC-relative literals on R2.
Alexey Frunze06a46c42016-07-19 15:00:40 -07007772 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007773 case HLoadClass::LoadKind::kBootImageAddress:
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007774 case HLoadClass::LoadKind::kBootImageClassTable:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007775 case HLoadClass::LoadKind::kBssEntry:
Alexey Frunzec61c0762017-04-10 13:54:23 -07007776 if (isR6) {
Alexey Frunze06a46c42016-07-19 15:00:40 -07007777 break;
7778 }
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007779 if (has_irreducible_loops) {
Alexey Frunze3b8c82f2017-10-10 23:01:34 -07007780 if (load_kind != HLoadClass::LoadKind::kBootImageAddress) {
7781 codegen_->ClobberRA();
7782 }
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007783 break;
7784 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07007785 FALLTHROUGH_INTENDED;
Alexey Frunze06a46c42016-07-19 15:00:40 -07007786 case HLoadClass::LoadKind::kReferrersClass:
Alexey Frunze06a46c42016-07-19 15:00:40 -07007787 locations->SetInAt(0, Location::RequiresRegister());
7788 break;
7789 default:
7790 break;
7791 }
7792 locations->SetOut(Location::RequiresRegister());
Alexey Frunzec61c0762017-04-10 13:54:23 -07007793 if (load_kind == HLoadClass::LoadKind::kBssEntry) {
7794 if (!kUseReadBarrier || kUseBakerReadBarrier) {
7795 // Rely on the type resolution or initialization and marking to save everything we need.
Alexey Frunzec61c0762017-04-10 13:54:23 -07007796 RegisterSet caller_saves = RegisterSet::Empty();
7797 InvokeRuntimeCallingConvention calling_convention;
7798 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
7799 locations->SetCustomSlowPathCallerSaves(caller_saves);
7800 } else {
7801 // For non-Baker read barriers we have a temp-clobbering call.
7802 }
7803 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007804}
7805
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007806// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7807// move.
7808void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
Vladimir Marko41559982017-01-06 14:04:23 +00007809 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007810 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Vladimir Marko41559982017-01-06 14:04:23 +00007811 codegen_->GenerateLoadClassRuntimeCall(cls);
Pavle Batutae87a7182015-10-28 13:10:42 +01007812 return;
7813 }
Vladimir Marko41559982017-01-06 14:04:23 +00007814 DCHECK(!cls->NeedsAccessCheck());
Pavle Batutae87a7182015-10-28 13:10:42 +01007815
Vladimir Marko41559982017-01-06 14:04:23 +00007816 LocationSummary* locations = cls->GetLocations();
Alexey Frunze06a46c42016-07-19 15:00:40 -07007817 Location out_loc = locations->Out();
7818 Register out = out_loc.AsRegister<Register>();
7819 Register base_or_current_method_reg;
7820 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007821 bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
Alexey Frunze06a46c42016-07-19 15:00:40 -07007822 switch (load_kind) {
7823 // We need an extra register for PC-relative literals on R2.
Alexey Frunze06a46c42016-07-19 15:00:40 -07007824 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007825 case HLoadClass::LoadKind::kBootImageAddress:
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007826 case HLoadClass::LoadKind::kBootImageClassTable:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007827 case HLoadClass::LoadKind::kBssEntry:
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007828 base_or_current_method_reg =
7829 (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>();
Alexey Frunze06a46c42016-07-19 15:00:40 -07007830 break;
Alexey Frunze06a46c42016-07-19 15:00:40 -07007831 case HLoadClass::LoadKind::kReferrersClass:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007832 case HLoadClass::LoadKind::kRuntimeCall:
Alexey Frunze06a46c42016-07-19 15:00:40 -07007833 base_or_current_method_reg = locations->InAt(0).AsRegister<Register>();
7834 break;
7835 default:
7836 base_or_current_method_reg = ZERO;
7837 break;
7838 }
Nicolas Geoffray42e372e2015-11-24 15:48:56 +00007839
Alexey Frunze15958152017-02-09 19:08:30 -08007840 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
7841 ? kWithoutReadBarrier
7842 : kCompilerReadBarrierOption;
Alexey Frunze06a46c42016-07-19 15:00:40 -07007843 bool generate_null_check = false;
7844 switch (load_kind) {
7845 case HLoadClass::LoadKind::kReferrersClass: {
7846 DCHECK(!cls->CanCallRuntime());
7847 DCHECK(!cls->MustGenerateClinitCheck());
7848 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
7849 GenerateGcRootFieldLoad(cls,
7850 out_loc,
7851 base_or_current_method_reg,
Alexey Frunze15958152017-02-09 19:08:30 -08007852 ArtMethod::DeclaringClassOffset().Int32Value(),
7853 read_barrier_option);
Alexey Frunze06a46c42016-07-19 15:00:40 -07007854 break;
7855 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07007856 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007857 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
Alexey Frunze15958152017-02-09 19:08:30 -08007858 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
Alexey Frunze5fa5c042017-06-01 21:07:52 -07007859 CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
Alexey Frunze06a46c42016-07-19 15:00:40 -07007860 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
Alexey Frunze5fa5c042017-06-01 21:07:52 -07007861 CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
7862 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
Alexey Frunze5fa5c042017-06-01 21:07:52 -07007863 codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
7864 out,
Alexey Frunzea663d9d2017-07-31 18:43:18 -07007865 base_or_current_method_reg);
7866 __ Addiu(out, out, /* placeholder */ 0x5678, &info_low->label);
Alexey Frunze06a46c42016-07-19 15:00:40 -07007867 break;
7868 }
7869 case HLoadClass::LoadKind::kBootImageAddress: {
Alexey Frunze15958152017-02-09 19:08:30 -08007870 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007871 uint32_t address = dchecked_integral_cast<uint32_t>(
7872 reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
7873 DCHECK_NE(address, 0u);
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007874 if (isR6 || !has_irreducible_loops) {
7875 __ LoadLiteral(out,
7876 base_or_current_method_reg,
7877 codegen_->DeduplicateBootImageAddressLiteral(address));
7878 } else {
7879 __ LoadConst32(out, address);
7880 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07007881 break;
7882 }
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007883 case HLoadClass::LoadKind::kBootImageClassTable: {
7884 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7885 CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
7886 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
7887 CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
7888 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
7889 codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
7890 out,
7891 base_or_current_method_reg);
7892 __ Lw(out, out, /* placeholder */ 0x5678, &info_low->label);
7893 // Extract the reference from the slot data, i.e. clear the hash bits.
7894 int32_t masked_hash = ClassTable::TableSlot::MaskHash(
7895 ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
7896 if (masked_hash != 0) {
7897 __ Addiu(out, out, -masked_hash);
7898 }
7899 break;
7900 }
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007901 case HLoadClass::LoadKind::kBssEntry: {
Vladimir Markof3c52b42017-11-17 17:32:12 +00007902 CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high =
7903 codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
Alexey Frunze5fa5c042017-06-01 21:07:52 -07007904 CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
7905 codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high);
Alexey Frunze5fa5c042017-06-01 21:07:52 -07007906 codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high,
Vladimir Markof3c52b42017-11-17 17:32:12 +00007907 out,
Alexey Frunze4147fcc2017-06-17 19:57:27 -07007908 base_or_current_method_reg);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07007909 GenerateGcRootFieldLoad(cls,
7910 out_loc,
Vladimir Markof3c52b42017-11-17 17:32:12 +00007911 out,
Alexey Frunze4147fcc2017-06-17 19:57:27 -07007912 /* placeholder */ 0x5678,
7913 read_barrier_option,
7914 &info_low->label);
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007915 generate_null_check = true;
7916 break;
7917 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007918 case HLoadClass::LoadKind::kJitTableAddress: {
Alexey Frunze627c1a02017-01-30 19:28:14 -08007919 CodeGeneratorMIPS::JitPatchInfo* info = codegen_->NewJitRootClassPatch(cls->GetDexFile(),
7920 cls->GetTypeIndex(),
7921 cls->GetClass());
7922 bool reordering = __ SetReorder(false);
7923 __ Bind(&info->high_label);
7924 __ Lui(out, /* placeholder */ 0x1234);
Alexey Frunze627c1a02017-01-30 19:28:14 -08007925 __ SetReorder(reordering);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07007926 GenerateGcRootFieldLoad(cls,
7927 out_loc,
7928 out,
7929 /* placeholder */ 0x5678,
7930 read_barrier_option,
7931 &info->low_label);
Alexey Frunze06a46c42016-07-19 15:00:40 -07007932 break;
7933 }
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007934 case HLoadClass::LoadKind::kRuntimeCall:
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007935 case HLoadClass::LoadKind::kInvalid:
Vladimir Marko41559982017-01-06 14:04:23 +00007936 LOG(FATAL) << "UNREACHABLE";
7937 UNREACHABLE();
Alexey Frunze06a46c42016-07-19 15:00:40 -07007938 }
7939
7940 if (generate_null_check || cls->MustGenerateClinitCheck()) {
7941 DCHECK(cls->CanCallRuntime());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007942 SlowPathCodeMIPS* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS(
Vladimir Markof3c52b42017-11-17 17:32:12 +00007943 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
Alexey Frunze06a46c42016-07-19 15:00:40 -07007944 codegen_->AddSlowPath(slow_path);
7945 if (generate_null_check) {
7946 __ Beqz(out, slow_path->GetEntryLabel());
7947 }
7948 if (cls->MustGenerateClinitCheck()) {
7949 GenerateClassInitializationCheck(slow_path, out);
7950 } else {
7951 __ Bind(slow_path->GetExitLabel());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007952 }
7953 }
7954}
7955
7956static int32_t GetExceptionTlsOffset() {
Andreas Gampe542451c2016-07-26 09:02:02 -07007957 return Thread::ExceptionOffset<kMipsPointerSize>().Int32Value();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007958}
7959
7960void LocationsBuilderMIPS::VisitLoadException(HLoadException* load) {
7961 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007962 new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007963 locations->SetOut(Location::RequiresRegister());
7964}
7965
7966void InstructionCodeGeneratorMIPS::VisitLoadException(HLoadException* load) {
7967 Register out = load->GetLocations()->Out().AsRegister<Register>();
7968 __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset());
7969}
7970
7971void LocationsBuilderMIPS::VisitClearException(HClearException* clear) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007972 new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007973}
7974
7975void InstructionCodeGeneratorMIPS::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
7976 __ StoreToOffset(kStoreWord, ZERO, TR, GetExceptionTlsOffset());
7977}
7978
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02007979void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
Alexey Frunzef63f5692016-12-13 17:43:11 -08007980 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Vladimir Markoca6fff82017-10-03 14:49:14 +01007981 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
Alexey Frunze06a46c42016-07-19 15:00:40 -07007982 HLoadString::LoadKind load_kind = load->GetLoadKind();
Alexey Frunzec61c0762017-04-10 13:54:23 -07007983 const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007984 const bool has_irreducible_loops = codegen_->GetGraph()->HasIrreducibleLoops();
Alexey Frunze06a46c42016-07-19 15:00:40 -07007985 switch (load_kind) {
7986 // We need an extra register for PC-relative literals on R2.
Alexey Frunze06a46c42016-07-19 15:00:40 -07007987 case HLoadString::LoadKind::kBootImageAddress:
7988 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007989 case HLoadString::LoadKind::kBootImageInternTable:
Vladimir Markoaad75c62016-10-03 08:46:48 +00007990 case HLoadString::LoadKind::kBssEntry:
Alexey Frunzec61c0762017-04-10 13:54:23 -07007991 if (isR6) {
Alexey Frunze06a46c42016-07-19 15:00:40 -07007992 break;
7993 }
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007994 if (has_irreducible_loops) {
Alexey Frunze3b8c82f2017-10-10 23:01:34 -07007995 if (load_kind != HLoadString::LoadKind::kBootImageAddress) {
7996 codegen_->ClobberRA();
7997 }
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02007998 break;
7999 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07008000 FALLTHROUGH_INTENDED;
8001 // We need an extra register for PC-relative dex cache accesses.
Vladimir Marko847e6ce2017-06-02 13:55:07 +01008002 case HLoadString::LoadKind::kRuntimeCall:
Alexey Frunze06a46c42016-07-19 15:00:40 -07008003 locations->SetInAt(0, Location::RequiresRegister());
8004 break;
8005 default:
8006 break;
8007 }
Vladimir Marko847e6ce2017-06-02 13:55:07 +01008008 if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
Alexey Frunzebb51df82016-11-01 16:07:32 -07008009 InvokeRuntimeCallingConvention calling_convention;
Alexey Frunzec61c0762017-04-10 13:54:23 -07008010 locations->SetOut(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
Alexey Frunzebb51df82016-11-01 16:07:32 -07008011 } else {
8012 locations->SetOut(Location::RequiresRegister());
Alexey Frunzec61c0762017-04-10 13:54:23 -07008013 if (load_kind == HLoadString::LoadKind::kBssEntry) {
8014 if (!kUseReadBarrier || kUseBakerReadBarrier) {
8015 // Rely on the pResolveString and marking to save everything we need.
Alexey Frunzec61c0762017-04-10 13:54:23 -07008016 RegisterSet caller_saves = RegisterSet::Empty();
8017 InvokeRuntimeCallingConvention calling_convention;
8018 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
8019 locations->SetCustomSlowPathCallerSaves(caller_saves);
8020 } else {
8021 // For non-Baker read barriers we have a temp-clobbering call.
8022 }
8023 }
Alexey Frunzebb51df82016-11-01 16:07:32 -07008024 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008025}
8026
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00008027// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
8028// move.
8029void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
Alexey Frunze06a46c42016-07-19 15:00:40 -07008030 HLoadString::LoadKind load_kind = load->GetLoadKind();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008031 LocationSummary* locations = load->GetLocations();
Alexey Frunze06a46c42016-07-19 15:00:40 -07008032 Location out_loc = locations->Out();
8033 Register out = out_loc.AsRegister<Register>();
8034 Register base_or_current_method_reg;
8035 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02008036 bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
Alexey Frunze06a46c42016-07-19 15:00:40 -07008037 switch (load_kind) {
8038 // We need an extra register for PC-relative literals on R2.
Alexey Frunze06a46c42016-07-19 15:00:40 -07008039 case HLoadString::LoadKind::kBootImageAddress:
8040 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01008041 case HLoadString::LoadKind::kBootImageInternTable:
Vladimir Markoaad75c62016-10-03 08:46:48 +00008042 case HLoadString::LoadKind::kBssEntry:
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02008043 base_or_current_method_reg =
8044 (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>();
Alexey Frunze06a46c42016-07-19 15:00:40 -07008045 break;
Alexey Frunze06a46c42016-07-19 15:00:40 -07008046 default:
8047 base_or_current_method_reg = ZERO;
8048 break;
8049 }
8050
8051 switch (load_kind) {
Alexey Frunze06a46c42016-07-19 15:00:40 -07008052 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Markoaad75c62016-10-03 08:46:48 +00008053 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
Alexey Frunze5fa5c042017-06-01 21:07:52 -07008054 CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
Vladimir Marko6bec91c2017-01-09 15:03:12 +00008055 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
Alexey Frunze5fa5c042017-06-01 21:07:52 -07008056 CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
8057 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
Alexey Frunze5fa5c042017-06-01 21:07:52 -07008058 codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
8059 out,
Alexey Frunzea663d9d2017-07-31 18:43:18 -07008060 base_or_current_method_reg);
8061 __ Addiu(out, out, /* placeholder */ 0x5678, &info_low->label);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01008062 return;
Alexey Frunze06a46c42016-07-19 15:00:40 -07008063 }
8064 case HLoadString::LoadKind::kBootImageAddress: {
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00008065 uint32_t address = dchecked_integral_cast<uint32_t>(
8066 reinterpret_cast<uintptr_t>(load->GetString().Get()));
8067 DCHECK_NE(address, 0u);
Goran Jakovljevicdebb5102017-09-21 14:24:06 +02008068 if (isR6 || !has_irreducible_loops) {
8069 __ LoadLiteral(out,
8070 base_or_current_method_reg,
8071 codegen_->DeduplicateBootImageAddressLiteral(address));
8072 } else {
8073 __ LoadConst32(out, address);
8074 }
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01008075 return;
Alexey Frunze06a46c42016-07-19 15:00:40 -07008076 }
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01008077 case HLoadString::LoadKind::kBootImageInternTable: {
Vladimir Markoaad75c62016-10-03 08:46:48 +00008078 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
Alexey Frunze5fa5c042017-06-01 21:07:52 -07008079 CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
Vladimir Marko6bec91c2017-01-09 15:03:12 +00008080 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
Alexey Frunze5fa5c042017-06-01 21:07:52 -07008081 CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
8082 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01008083 codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
8084 out,
8085 base_or_current_method_reg);
8086 __ Lw(out, out, /* placeholder */ 0x5678, &info_low->label);
8087 return;
8088 }
8089 case HLoadString::LoadKind::kBssEntry: {
8090 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
8091 CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
8092 codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
8093 CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
8094 codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
Alexey Frunze5fa5c042017-06-01 21:07:52 -07008095 codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
Vladimir Markof3c52b42017-11-17 17:32:12 +00008096 out,
Alexey Frunze4147fcc2017-06-17 19:57:27 -07008097 base_or_current_method_reg);
Alexey Frunze5fa5c042017-06-01 21:07:52 -07008098 GenerateGcRootFieldLoad(load,
8099 out_loc,
Vladimir Markof3c52b42017-11-17 17:32:12 +00008100 out,
Alexey Frunze5fa5c042017-06-01 21:07:52 -07008101 /* placeholder */ 0x5678,
Alexey Frunze4147fcc2017-06-17 19:57:27 -07008102 kCompilerReadBarrierOption,
8103 &info_low->label);
Alexey Frunze5fa5c042017-06-01 21:07:52 -07008104 SlowPathCodeMIPS* slow_path =
Vladimir Markof3c52b42017-11-17 17:32:12 +00008105 new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS(load);
Vladimir Markoaad75c62016-10-03 08:46:48 +00008106 codegen_->AddSlowPath(slow_path);
8107 __ Beqz(out, slow_path->GetEntryLabel());
8108 __ Bind(slow_path->GetExitLabel());
8109 return;
8110 }
Alexey Frunze627c1a02017-01-30 19:28:14 -08008111 case HLoadString::LoadKind::kJitTableAddress: {
8112 CodeGeneratorMIPS::JitPatchInfo* info =
8113 codegen_->NewJitRootStringPatch(load->GetDexFile(),
8114 load->GetStringIndex(),
8115 load->GetString());
8116 bool reordering = __ SetReorder(false);
8117 __ Bind(&info->high_label);
8118 __ Lui(out, /* placeholder */ 0x1234);
Alexey Frunze4147fcc2017-06-17 19:57:27 -07008119 __ SetReorder(reordering);
Alexey Frunze15958152017-02-09 19:08:30 -08008120 GenerateGcRootFieldLoad(load,
8121 out_loc,
8122 out,
8123 /* placeholder */ 0x5678,
Alexey Frunze4147fcc2017-06-17 19:57:27 -07008124 kCompilerReadBarrierOption,
8125 &info->low_label);
Alexey Frunze627c1a02017-01-30 19:28:14 -08008126 return;
8127 }
Alexey Frunze06a46c42016-07-19 15:00:40 -07008128 default:
Christina Wadsworthbf44e0e2016-08-18 10:37:42 -07008129 break;
Alexey Frunze06a46c42016-07-19 15:00:40 -07008130 }
Nicolas Geoffray917d0162015-11-24 18:25:35 +00008131
Christina Wadsworthbf44e0e2016-08-18 10:37:42 -07008132 // TODO: Re-add the compiler code to do string dex cache lookup again.
Vladimir Marko847e6ce2017-06-02 13:55:07 +01008133 DCHECK(load_kind == HLoadString::LoadKind::kRuntimeCall);
Vladimir Markoaad75c62016-10-03 08:46:48 +00008134 InvokeRuntimeCallingConvention calling_convention;
Alexey Frunzec61c0762017-04-10 13:54:23 -07008135 DCHECK_EQ(calling_convention.GetRegisterAt(0), out);
Andreas Gampe8a0128a2016-11-28 07:38:35 -08008136 __ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
Vladimir Markoaad75c62016-10-03 08:46:48 +00008137 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
8138 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008139}
8140
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008141void LocationsBuilderMIPS::VisitLongConstant(HLongConstant* constant) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008142 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008143 locations->SetOut(Location::ConstantLocation(constant));
8144}
8145
8146void InstructionCodeGeneratorMIPS::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
8147 // Will be generated at use site.
8148}
8149
8150void LocationsBuilderMIPS::VisitMonitorOperation(HMonitorOperation* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008151 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8152 instruction, LocationSummary::kCallOnMainOnly);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008153 InvokeRuntimeCallingConvention calling_convention;
8154 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
8155}
8156
8157void InstructionCodeGeneratorMIPS::VisitMonitorOperation(HMonitorOperation* instruction) {
8158 if (instruction->IsEnter()) {
Serban Constantinescufca16662016-07-14 09:21:59 +01008159 codegen_->InvokeRuntime(kQuickLockObject, instruction, instruction->GetDexPc());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008160 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
8161 } else {
Serban Constantinescufca16662016-07-14 09:21:59 +01008162 codegen_->InvokeRuntime(kQuickUnlockObject, instruction, instruction->GetDexPc());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008163 }
8164 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
8165}
8166
8167void LocationsBuilderMIPS::VisitMul(HMul* mul) {
8168 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008169 new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008170 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008171 case DataType::Type::kInt32:
8172 case DataType::Type::kInt64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008173 locations->SetInAt(0, Location::RequiresRegister());
8174 locations->SetInAt(1, Location::RequiresRegister());
8175 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8176 break;
8177
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008178 case DataType::Type::kFloat32:
8179 case DataType::Type::kFloat64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008180 locations->SetInAt(0, Location::RequiresFpuRegister());
8181 locations->SetInAt(1, Location::RequiresFpuRegister());
8182 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
8183 break;
8184
8185 default:
8186 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
8187 }
8188}
8189
8190void InstructionCodeGeneratorMIPS::VisitMul(HMul* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008191 DataType::Type type = instruction->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008192 LocationSummary* locations = instruction->GetLocations();
8193 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
8194
8195 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008196 case DataType::Type::kInt32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008197 Register dst = locations->Out().AsRegister<Register>();
8198 Register lhs = locations->InAt(0).AsRegister<Register>();
8199 Register rhs = locations->InAt(1).AsRegister<Register>();
8200
8201 if (isR6) {
8202 __ MulR6(dst, lhs, rhs);
8203 } else {
8204 __ MulR2(dst, lhs, rhs);
8205 }
8206 break;
8207 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008208 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008209 Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
8210 Register dst_low = locations->Out().AsRegisterPairLow<Register>();
8211 Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
8212 Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
8213 Register rhs_high = locations->InAt(1).AsRegisterPairHigh<Register>();
8214 Register rhs_low = locations->InAt(1).AsRegisterPairLow<Register>();
8215
8216 // Extra checks to protect caused by the existance of A1_A2.
8217 // The algorithm is wrong if dst_high is either lhs_lo or rhs_lo:
8218 // (e.g. lhs=a0_a1, rhs=a2_a3 and dst=a1_a2).
8219 DCHECK_NE(dst_high, lhs_low);
8220 DCHECK_NE(dst_high, rhs_low);
8221
8222 // A_B * C_D
8223 // dst_hi: [ low(A*D) + low(B*C) + hi(B*D) ]
8224 // dst_lo: [ low(B*D) ]
8225 // Note: R2 and R6 MUL produce the low 32 bit of the multiplication result.
8226
8227 if (isR6) {
8228 __ MulR6(TMP, lhs_high, rhs_low);
8229 __ MulR6(dst_high, lhs_low, rhs_high);
8230 __ Addu(dst_high, dst_high, TMP);
8231 __ MuhuR6(TMP, lhs_low, rhs_low);
8232 __ Addu(dst_high, dst_high, TMP);
8233 __ MulR6(dst_low, lhs_low, rhs_low);
8234 } else {
8235 __ MulR2(TMP, lhs_high, rhs_low);
8236 __ MulR2(dst_high, lhs_low, rhs_high);
8237 __ Addu(dst_high, dst_high, TMP);
8238 __ MultuR2(lhs_low, rhs_low);
8239 __ Mfhi(TMP);
8240 __ Addu(dst_high, dst_high, TMP);
8241 __ Mflo(dst_low);
8242 }
8243 break;
8244 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008245 case DataType::Type::kFloat32:
8246 case DataType::Type::kFloat64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008247 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
8248 FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
8249 FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008250 if (type == DataType::Type::kFloat32) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008251 __ MulS(dst, lhs, rhs);
8252 } else {
8253 __ MulD(dst, lhs, rhs);
8254 }
8255 break;
8256 }
8257 default:
8258 LOG(FATAL) << "Unexpected mul type " << type;
8259 }
8260}
8261
8262void LocationsBuilderMIPS::VisitNeg(HNeg* neg) {
8263 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008264 new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008265 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008266 case DataType::Type::kInt32:
8267 case DataType::Type::kInt64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008268 locations->SetInAt(0, Location::RequiresRegister());
8269 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8270 break;
8271
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008272 case DataType::Type::kFloat32:
8273 case DataType::Type::kFloat64:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008274 locations->SetInAt(0, Location::RequiresFpuRegister());
8275 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
8276 break;
8277
8278 default:
8279 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
8280 }
8281}
8282
8283void InstructionCodeGeneratorMIPS::VisitNeg(HNeg* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008284 DataType::Type type = instruction->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008285 LocationSummary* locations = instruction->GetLocations();
8286
8287 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008288 case DataType::Type::kInt32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008289 Register dst = locations->Out().AsRegister<Register>();
8290 Register src = locations->InAt(0).AsRegister<Register>();
8291 __ Subu(dst, ZERO, src);
8292 break;
8293 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008294 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008295 Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
8296 Register dst_low = locations->Out().AsRegisterPairLow<Register>();
8297 Register src_high = locations->InAt(0).AsRegisterPairHigh<Register>();
8298 Register src_low = locations->InAt(0).AsRegisterPairLow<Register>();
8299 __ Subu(dst_low, ZERO, src_low);
8300 __ Sltu(TMP, ZERO, dst_low);
8301 __ Subu(dst_high, ZERO, src_high);
8302 __ Subu(dst_high, dst_high, TMP);
8303 break;
8304 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008305 case DataType::Type::kFloat32:
8306 case DataType::Type::kFloat64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008307 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
8308 FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008309 if (type == DataType::Type::kFloat32) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008310 __ NegS(dst, src);
8311 } else {
8312 __ NegD(dst, src);
8313 }
8314 break;
8315 }
8316 default:
8317 LOG(FATAL) << "Unexpected neg type " << type;
8318 }
8319}
8320
8321void LocationsBuilderMIPS::VisitNewArray(HNewArray* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008322 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8323 instruction, LocationSummary::kCallOnMainOnly);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008324 InvokeRuntimeCallingConvention calling_convention;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008325 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00008326 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
8327 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008328}
8329
8330void InstructionCodeGeneratorMIPS::VisitNewArray(HNewArray* instruction) {
Alexey Frunzec061de12017-02-14 13:27:23 -08008331 // Note: if heap poisoning is enabled, the entry point takes care
8332 // of poisoning the reference.
Goran Jakovljevic854df412017-06-27 14:41:39 +02008333 QuickEntrypointEnum entrypoint =
8334 CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
8335 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00008336 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
Goran Jakovljevic854df412017-06-27 14:41:39 +02008337 DCHECK(!codegen_->IsLeafMethod());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008338}
8339
8340void LocationsBuilderMIPS::VisitNewInstance(HNewInstance* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008341 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8342 instruction, LocationSummary::kCallOnMainOnly);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008343 InvokeRuntimeCallingConvention calling_convention;
David Brazdil6de19382016-01-08 17:37:10 +00008344 if (instruction->IsStringAlloc()) {
8345 locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument));
8346 } else {
8347 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
David Brazdil6de19382016-01-08 17:37:10 +00008348 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008349 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008350}
8351
8352void InstructionCodeGeneratorMIPS::VisitNewInstance(HNewInstance* instruction) {
Alexey Frunzec061de12017-02-14 13:27:23 -08008353 // Note: if heap poisoning is enabled, the entry point takes care
8354 // of poisoning the reference.
David Brazdil6de19382016-01-08 17:37:10 +00008355 if (instruction->IsStringAlloc()) {
8356 // String is allocated through StringFactory. Call NewEmptyString entry point.
8357 Register temp = instruction->GetLocations()->GetTemp(0).AsRegister<Register>();
Andreas Gampe542451c2016-07-26 09:02:02 -07008358 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMipsPointerSize);
David Brazdil6de19382016-01-08 17:37:10 +00008359 __ LoadFromOffset(kLoadWord, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString));
8360 __ LoadFromOffset(kLoadWord, T9, temp, code_offset.Int32Value());
8361 __ Jalr(T9);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07008362 __ NopIfNoReordering();
David Brazdil6de19382016-01-08 17:37:10 +00008363 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
8364 } else {
Serban Constantinescufca16662016-07-14 09:21:59 +01008365 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
Nicolas Geoffray0d3998b2017-01-12 15:35:12 +00008366 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
David Brazdil6de19382016-01-08 17:37:10 +00008367 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008368}
8369
8370void LocationsBuilderMIPS::VisitNot(HNot* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008371 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008372 locations->SetInAt(0, Location::RequiresRegister());
8373 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8374}
8375
8376void InstructionCodeGeneratorMIPS::VisitNot(HNot* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008377 DataType::Type type = instruction->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008378 LocationSummary* locations = instruction->GetLocations();
8379
8380 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008381 case DataType::Type::kInt32: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008382 Register dst = locations->Out().AsRegister<Register>();
8383 Register src = locations->InAt(0).AsRegister<Register>();
8384 __ Nor(dst, src, ZERO);
8385 break;
8386 }
8387
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008388 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008389 Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
8390 Register dst_low = locations->Out().AsRegisterPairLow<Register>();
8391 Register src_high = locations->InAt(0).AsRegisterPairHigh<Register>();
8392 Register src_low = locations->InAt(0).AsRegisterPairLow<Register>();
8393 __ Nor(dst_high, src_high, ZERO);
8394 __ Nor(dst_low, src_low, ZERO);
8395 break;
8396 }
8397
8398 default:
8399 LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType();
8400 }
8401}
8402
8403void LocationsBuilderMIPS::VisitBooleanNot(HBooleanNot* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008404 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008405 locations->SetInAt(0, Location::RequiresRegister());
8406 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8407}
8408
8409void InstructionCodeGeneratorMIPS::VisitBooleanNot(HBooleanNot* instruction) {
8410 LocationSummary* locations = instruction->GetLocations();
8411 __ Xori(locations->Out().AsRegister<Register>(),
8412 locations->InAt(0).AsRegister<Register>(),
8413 1);
8414}
8415
8416void LocationsBuilderMIPS::VisitNullCheck(HNullCheck* instruction) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01008417 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
8418 locations->SetInAt(0, Location::RequiresRegister());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008419}
8420
Calin Juravle2ae48182016-03-16 14:05:09 +00008421void CodeGeneratorMIPS::GenerateImplicitNullCheck(HNullCheck* instruction) {
8422 if (CanMoveNullCheckToUser(instruction)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008423 return;
8424 }
8425 Location obj = instruction->GetLocations()->InAt(0);
8426
8427 __ Lw(ZERO, obj.AsRegister<Register>(), 0);
Calin Juravle2ae48182016-03-16 14:05:09 +00008428 RecordPcInfo(instruction, instruction->GetDexPc());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008429}
8430
Calin Juravle2ae48182016-03-16 14:05:09 +00008431void CodeGeneratorMIPS::GenerateExplicitNullCheck(HNullCheck* instruction) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01008432 SlowPathCodeMIPS* slow_path = new (GetScopedAllocator()) NullCheckSlowPathMIPS(instruction);
Calin Juravle2ae48182016-03-16 14:05:09 +00008433 AddSlowPath(slow_path);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008434
8435 Location obj = instruction->GetLocations()->InAt(0);
8436
8437 __ Beqz(obj.AsRegister<Register>(), slow_path->GetEntryLabel());
8438}
8439
8440void InstructionCodeGeneratorMIPS::VisitNullCheck(HNullCheck* instruction) {
Calin Juravle2ae48182016-03-16 14:05:09 +00008441 codegen_->GenerateNullCheck(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008442}
8443
8444void LocationsBuilderMIPS::VisitOr(HOr* instruction) {
8445 HandleBinaryOp(instruction);
8446}
8447
8448void InstructionCodeGeneratorMIPS::VisitOr(HOr* instruction) {
8449 HandleBinaryOp(instruction);
8450}
8451
8452void LocationsBuilderMIPS::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
8453 LOG(FATAL) << "Unreachable";
8454}
8455
8456void InstructionCodeGeneratorMIPS::VisitParallelMove(HParallelMove* instruction) {
Vladimir Markobea75ff2017-10-11 20:39:54 +01008457 if (instruction->GetNext()->IsSuspendCheck() &&
8458 instruction->GetBlock()->GetLoopInformation() != nullptr) {
8459 HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
8460 // The back edge will generate the suspend check.
8461 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
8462 }
8463
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008464 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
8465}
8466
8467void LocationsBuilderMIPS::VisitParameterValue(HParameterValue* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008468 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008469 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
8470 if (location.IsStackSlot()) {
8471 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
8472 } else if (location.IsDoubleStackSlot()) {
8473 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
8474 }
8475 locations->SetOut(location);
8476}
8477
8478void InstructionCodeGeneratorMIPS::VisitParameterValue(HParameterValue* instruction
8479 ATTRIBUTE_UNUSED) {
8480 // Nothing to do, the parameter is already at its location.
8481}
8482
8483void LocationsBuilderMIPS::VisitCurrentMethod(HCurrentMethod* instruction) {
8484 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008485 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008486 locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument));
8487}
8488
8489void InstructionCodeGeneratorMIPS::VisitCurrentMethod(HCurrentMethod* instruction
8490 ATTRIBUTE_UNUSED) {
8491 // Nothing to do, the method is already at its location.
8492}
8493
8494void LocationsBuilderMIPS::VisitPhi(HPhi* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008495 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Vladimir Marko372f10e2016-05-17 16:30:10 +01008496 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008497 locations->SetInAt(i, Location::Any());
8498 }
8499 locations->SetOut(Location::Any());
8500}
8501
8502void InstructionCodeGeneratorMIPS::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
8503 LOG(FATAL) << "Unreachable";
8504}
8505
8506void LocationsBuilderMIPS::VisitRem(HRem* rem) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008507 DataType::Type type = rem->GetResultType();
8508 LocationSummary::CallKind call_kind = (type == DataType::Type::kInt32)
8509 ? LocationSummary::kNoCall
8510 : LocationSummary::kCallOnMainOnly;
Vladimir Markoca6fff82017-10-03 14:49:14 +01008511 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008512
8513 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008514 case DataType::Type::kInt32:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008515 locations->SetInAt(0, Location::RequiresRegister());
Alexey Frunze7e99e052015-11-24 19:28:01 -08008516 locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008517 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8518 break;
8519
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008520 case DataType::Type::kInt64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008521 InvokeRuntimeCallingConvention calling_convention;
8522 locations->SetInAt(0, Location::RegisterPairLocation(
8523 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
8524 locations->SetInAt(1, Location::RegisterPairLocation(
8525 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
8526 locations->SetOut(calling_convention.GetReturnLocation(type));
8527 break;
8528 }
8529
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008530 case DataType::Type::kFloat32:
8531 case DataType::Type::kFloat64: {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008532 InvokeRuntimeCallingConvention calling_convention;
8533 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
8534 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
8535 locations->SetOut(calling_convention.GetReturnLocation(type));
8536 break;
8537 }
8538
8539 default:
8540 LOG(FATAL) << "Unexpected rem type " << type;
8541 }
8542}
8543
8544void InstructionCodeGeneratorMIPS::VisitRem(HRem* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008545 DataType::Type type = instruction->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008546
8547 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008548 case DataType::Type::kInt32:
Alexey Frunze7e99e052015-11-24 19:28:01 -08008549 GenerateDivRemIntegral(instruction);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008550 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008551 case DataType::Type::kInt64: {
Serban Constantinescufca16662016-07-14 09:21:59 +01008552 codegen_->InvokeRuntime(kQuickLmod, instruction, instruction->GetDexPc());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008553 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
8554 break;
8555 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008556 case DataType::Type::kFloat32: {
Serban Constantinescufca16662016-07-14 09:21:59 +01008557 codegen_->InvokeRuntime(kQuickFmodf, instruction, instruction->GetDexPc());
Roland Levillain888d0672015-11-23 18:53:50 +00008558 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008559 break;
8560 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008561 case DataType::Type::kFloat64: {
Serban Constantinescufca16662016-07-14 09:21:59 +01008562 codegen_->InvokeRuntime(kQuickFmod, instruction, instruction->GetDexPc());
Roland Levillain888d0672015-11-23 18:53:50 +00008563 CheckEntrypointTypes<kQuickFmod, double, double, double>();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008564 break;
8565 }
8566 default:
8567 LOG(FATAL) << "Unexpected rem type " << type;
8568 }
8569}
8570
Igor Murashkind01745e2017-04-05 16:40:31 -07008571void LocationsBuilderMIPS::VisitConstructorFence(HConstructorFence* constructor_fence) {
8572 constructor_fence->SetLocations(nullptr);
8573}
8574
8575void InstructionCodeGeneratorMIPS::VisitConstructorFence(
8576 HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
8577 GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
8578}
8579
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008580void LocationsBuilderMIPS::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
8581 memory_barrier->SetLocations(nullptr);
8582}
8583
8584void InstructionCodeGeneratorMIPS::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
8585 GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
8586}
8587
8588void LocationsBuilderMIPS::VisitReturn(HReturn* ret) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008589 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(ret);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008590 DataType::Type return_type = ret->InputAt(0)->GetType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008591 locations->SetInAt(0, MipsReturnLocation(return_type));
8592}
8593
8594void InstructionCodeGeneratorMIPS::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
8595 codegen_->GenerateFrameExit();
8596}
8597
8598void LocationsBuilderMIPS::VisitReturnVoid(HReturnVoid* ret) {
8599 ret->SetLocations(nullptr);
8600}
8601
8602void InstructionCodeGeneratorMIPS::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
8603 codegen_->GenerateFrameExit();
8604}
8605
Alexey Frunze92d90602015-12-18 18:16:36 -08008606void LocationsBuilderMIPS::VisitRor(HRor* ror) {
8607 HandleShift(ror);
Scott Wakeling40a04bf2015-12-11 09:50:36 +00008608}
8609
Alexey Frunze92d90602015-12-18 18:16:36 -08008610void InstructionCodeGeneratorMIPS::VisitRor(HRor* ror) {
8611 HandleShift(ror);
Scott Wakeling40a04bf2015-12-11 09:50:36 +00008612}
8613
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008614void LocationsBuilderMIPS::VisitShl(HShl* shl) {
8615 HandleShift(shl);
8616}
8617
8618void InstructionCodeGeneratorMIPS::VisitShl(HShl* shl) {
8619 HandleShift(shl);
8620}
8621
8622void LocationsBuilderMIPS::VisitShr(HShr* shr) {
8623 HandleShift(shr);
8624}
8625
8626void InstructionCodeGeneratorMIPS::VisitShr(HShr* shr) {
8627 HandleShift(shr);
8628}
8629
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008630void LocationsBuilderMIPS::VisitSub(HSub* instruction) {
8631 HandleBinaryOp(instruction);
8632}
8633
8634void InstructionCodeGeneratorMIPS::VisitSub(HSub* instruction) {
8635 HandleBinaryOp(instruction);
8636}
8637
8638void LocationsBuilderMIPS::VisitStaticFieldGet(HStaticFieldGet* instruction) {
8639 HandleFieldGet(instruction, instruction->GetFieldInfo());
8640}
8641
8642void InstructionCodeGeneratorMIPS::VisitStaticFieldGet(HStaticFieldGet* instruction) {
8643 HandleFieldGet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc());
8644}
8645
8646void LocationsBuilderMIPS::VisitStaticFieldSet(HStaticFieldSet* instruction) {
8647 HandleFieldSet(instruction, instruction->GetFieldInfo());
8648}
8649
8650void InstructionCodeGeneratorMIPS::VisitStaticFieldSet(HStaticFieldSet* instruction) {
Goran Jakovljevice114da22016-12-26 14:21:43 +01008651 HandleFieldSet(instruction,
8652 instruction->GetFieldInfo(),
8653 instruction->GetDexPc(),
8654 instruction->GetValueCanBeNull());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008655}
8656
8657void LocationsBuilderMIPS::VisitUnresolvedInstanceFieldGet(
8658 HUnresolvedInstanceFieldGet* instruction) {
8659 FieldAccessCallingConventionMIPS calling_convention;
8660 codegen_->CreateUnresolvedFieldLocationSummary(instruction,
8661 instruction->GetFieldType(),
8662 calling_convention);
8663}
8664
8665void InstructionCodeGeneratorMIPS::VisitUnresolvedInstanceFieldGet(
8666 HUnresolvedInstanceFieldGet* instruction) {
8667 FieldAccessCallingConventionMIPS calling_convention;
8668 codegen_->GenerateUnresolvedFieldAccess(instruction,
8669 instruction->GetFieldType(),
8670 instruction->GetFieldIndex(),
8671 instruction->GetDexPc(),
8672 calling_convention);
8673}
8674
8675void LocationsBuilderMIPS::VisitUnresolvedInstanceFieldSet(
8676 HUnresolvedInstanceFieldSet* instruction) {
8677 FieldAccessCallingConventionMIPS calling_convention;
8678 codegen_->CreateUnresolvedFieldLocationSummary(instruction,
8679 instruction->GetFieldType(),
8680 calling_convention);
8681}
8682
8683void InstructionCodeGeneratorMIPS::VisitUnresolvedInstanceFieldSet(
8684 HUnresolvedInstanceFieldSet* instruction) {
8685 FieldAccessCallingConventionMIPS calling_convention;
8686 codegen_->GenerateUnresolvedFieldAccess(instruction,
8687 instruction->GetFieldType(),
8688 instruction->GetFieldIndex(),
8689 instruction->GetDexPc(),
8690 calling_convention);
8691}
8692
8693void LocationsBuilderMIPS::VisitUnresolvedStaticFieldGet(
8694 HUnresolvedStaticFieldGet* instruction) {
8695 FieldAccessCallingConventionMIPS calling_convention;
8696 codegen_->CreateUnresolvedFieldLocationSummary(instruction,
8697 instruction->GetFieldType(),
8698 calling_convention);
8699}
8700
8701void InstructionCodeGeneratorMIPS::VisitUnresolvedStaticFieldGet(
8702 HUnresolvedStaticFieldGet* instruction) {
8703 FieldAccessCallingConventionMIPS calling_convention;
8704 codegen_->GenerateUnresolvedFieldAccess(instruction,
8705 instruction->GetFieldType(),
8706 instruction->GetFieldIndex(),
8707 instruction->GetDexPc(),
8708 calling_convention);
8709}
8710
8711void LocationsBuilderMIPS::VisitUnresolvedStaticFieldSet(
8712 HUnresolvedStaticFieldSet* instruction) {
8713 FieldAccessCallingConventionMIPS calling_convention;
8714 codegen_->CreateUnresolvedFieldLocationSummary(instruction,
8715 instruction->GetFieldType(),
8716 calling_convention);
8717}
8718
8719void InstructionCodeGeneratorMIPS::VisitUnresolvedStaticFieldSet(
8720 HUnresolvedStaticFieldSet* instruction) {
8721 FieldAccessCallingConventionMIPS calling_convention;
8722 codegen_->GenerateUnresolvedFieldAccess(instruction,
8723 instruction->GetFieldType(),
8724 instruction->GetFieldIndex(),
8725 instruction->GetDexPc(),
8726 calling_convention);
8727}
8728
8729void LocationsBuilderMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008730 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8731 instruction, LocationSummary::kCallOnSlowPath);
Lena Djokicca8c2952017-05-29 11:31:46 +02008732 // In suspend check slow path, usually there are no caller-save registers at all.
8733 // If SIMD instructions are present, however, we force spilling all live SIMD
8734 // registers in full width (since the runtime only saves/restores lower part).
8735 locations->SetCustomSlowPathCallerSaves(
8736 GetGraph()->HasSIMD() ? RegisterSet::AllFpu() : RegisterSet::Empty());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008737}
8738
8739void InstructionCodeGeneratorMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
8740 HBasicBlock* block = instruction->GetBlock();
8741 if (block->GetLoopInformation() != nullptr) {
8742 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
8743 // The back edge will generate the suspend check.
8744 return;
8745 }
8746 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
8747 // The goto will generate the suspend check.
8748 return;
8749 }
8750 GenerateSuspendCheck(instruction, nullptr);
8751}
8752
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008753void LocationsBuilderMIPS::VisitThrow(HThrow* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008754 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8755 instruction, LocationSummary::kCallOnMainOnly);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008756 InvokeRuntimeCallingConvention calling_convention;
8757 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
8758}
8759
8760void InstructionCodeGeneratorMIPS::VisitThrow(HThrow* instruction) {
Serban Constantinescufca16662016-07-14 09:21:59 +01008761 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008762 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
8763}
8764
8765void LocationsBuilderMIPS::VisitTypeConversion(HTypeConversion* conversion) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008766 DataType::Type input_type = conversion->GetInputType();
8767 DataType::Type result_type = conversion->GetResultType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01008768 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
8769 << input_type << " -> " << result_type;
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008770 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008771
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008772 if ((input_type == DataType::Type::kReference) || (input_type == DataType::Type::kVoid) ||
8773 (result_type == DataType::Type::kReference) || (result_type == DataType::Type::kVoid)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008774 LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
8775 }
8776
8777 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008778 if (!isR6 &&
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008779 ((DataType::IsFloatingPointType(result_type) && input_type == DataType::Type::kInt64) ||
8780 (result_type == DataType::Type::kInt64 && DataType::IsFloatingPointType(input_type)))) {
Serban Constantinescu54ff4822016-07-07 18:03:19 +01008781 call_kind = LocationSummary::kCallOnMainOnly;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008782 }
8783
Vladimir Markoca6fff82017-10-03 14:49:14 +01008784 LocationSummary* locations =
8785 new (GetGraph()->GetAllocator()) LocationSummary(conversion, call_kind);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008786
8787 if (call_kind == LocationSummary::kNoCall) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008788 if (DataType::IsFloatingPointType(input_type)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008789 locations->SetInAt(0, Location::RequiresFpuRegister());
8790 } else {
8791 locations->SetInAt(0, Location::RequiresRegister());
8792 }
8793
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008794 if (DataType::IsFloatingPointType(result_type)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008795 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
8796 } else {
8797 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8798 }
8799 } else {
8800 InvokeRuntimeCallingConvention calling_convention;
8801
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008802 if (DataType::IsFloatingPointType(input_type)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008803 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
8804 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008805 DCHECK_EQ(input_type, DataType::Type::kInt64);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008806 locations->SetInAt(0, Location::RegisterPairLocation(
8807 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
8808 }
8809
8810 locations->SetOut(calling_convention.GetReturnLocation(result_type));
8811 }
8812}
8813
8814void InstructionCodeGeneratorMIPS::VisitTypeConversion(HTypeConversion* conversion) {
8815 LocationSummary* locations = conversion->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008816 DataType::Type result_type = conversion->GetResultType();
8817 DataType::Type input_type = conversion->GetInputType();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008818 bool has_sign_extension = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008819 bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008820
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01008821 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
8822 << input_type << " -> " << result_type;
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008823
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008824 if (result_type == DataType::Type::kInt64 && DataType::IsIntegralType(input_type)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008825 Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
8826 Register dst_low = locations->Out().AsRegisterPairLow<Register>();
8827 Register src = locations->InAt(0).AsRegister<Register>();
8828
Alexey Frunzea871ef12016-06-27 15:20:11 -07008829 if (dst_low != src) {
8830 __ Move(dst_low, src);
8831 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008832 __ Sra(dst_high, src, 31);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008833 } else if (DataType::IsIntegralType(result_type) && DataType::IsIntegralType(input_type)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008834 Register dst = locations->Out().AsRegister<Register>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008835 Register src = (input_type == DataType::Type::kInt64)
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008836 ? locations->InAt(0).AsRegisterPairLow<Register>()
8837 : locations->InAt(0).AsRegister<Register>();
8838
8839 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01008840 case DataType::Type::kUint8:
8841 __ Andi(dst, src, 0xFF);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008842 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008843 case DataType::Type::kInt8:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008844 if (has_sign_extension) {
8845 __ Seb(dst, src);
8846 } else {
8847 __ Sll(dst, src, 24);
8848 __ Sra(dst, dst, 24);
8849 }
8850 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01008851 case DataType::Type::kUint16:
8852 __ Andi(dst, src, 0xFFFF);
8853 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008854 case DataType::Type::kInt16:
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008855 if (has_sign_extension) {
8856 __ Seh(dst, src);
8857 } else {
8858 __ Sll(dst, src, 16);
8859 __ Sra(dst, dst, 16);
8860 }
8861 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008862 case DataType::Type::kInt32:
Alexey Frunzea871ef12016-06-27 15:20:11 -07008863 if (dst != src) {
8864 __ Move(dst, src);
8865 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008866 break;
8867
8868 default:
8869 LOG(FATAL) << "Unexpected type conversion from " << input_type
8870 << " to " << result_type;
8871 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008872 } else if (DataType::IsFloatingPointType(result_type) && DataType::IsIntegralType(input_type)) {
8873 if (input_type == DataType::Type::kInt64) {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008874 if (isR6) {
8875 // cvt.s.l/cvt.d.l requires MIPSR2+ with FR=1. MIPS32R6 is implemented as a secondary
8876 // architecture on top of MIPS64R6, which has FR=1, and therefore can use the instruction.
8877 Register src_high = locations->InAt(0).AsRegisterPairHigh<Register>();
8878 Register src_low = locations->InAt(0).AsRegisterPairLow<Register>();
8879 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
8880 __ Mtc1(src_low, FTMP);
8881 __ Mthc1(src_high, FTMP);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008882 if (result_type == DataType::Type::kFloat32) {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008883 __ Cvtsl(dst, FTMP);
8884 } else {
8885 __ Cvtdl(dst, FTMP);
8886 }
8887 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008888 QuickEntrypointEnum entrypoint =
8889 (result_type == DataType::Type::kFloat32) ? kQuickL2f : kQuickL2d;
Serban Constantinescufca16662016-07-14 09:21:59 +01008890 codegen_->InvokeRuntime(entrypoint, conversion, conversion->GetDexPc());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008891 if (result_type == DataType::Type::kFloat32) {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008892 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
8893 } else {
8894 CheckEntrypointTypes<kQuickL2d, double, int64_t>();
8895 }
8896 }
8897 } else {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008898 Register src = locations->InAt(0).AsRegister<Register>();
8899 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
8900 __ Mtc1(src, FTMP);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008901 if (result_type == DataType::Type::kFloat32) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008902 __ Cvtsw(dst, FTMP);
8903 } else {
8904 __ Cvtdw(dst, FTMP);
8905 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008906 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008907 } else if (DataType::IsIntegralType(result_type) && DataType::IsFloatingPointType(input_type)) {
8908 CHECK(result_type == DataType::Type::kInt32 || result_type == DataType::Type::kInt64);
Lena Djokicf4e23a82017-05-09 15:43:45 +02008909
8910 // When NAN2008=1 (R6), the truncate instruction caps the output at the minimum/maximum
8911 // value of the output type if the input is outside of the range after the truncation or
8912 // produces 0 when the input is a NaN. IOW, the three special cases produce three distinct
8913 // results. This matches the desired float/double-to-int/long conversion exactly.
8914 //
8915 // When NAN2008=0 (R2 and before), the truncate instruction produces the maximum positive
8916 // value when the input is either a NaN or is outside of the range of the output type
8917 // after the truncation. IOW, the three special cases (NaN, too small, too big) produce
8918 // the same result.
8919 //
8920 // The code takes care of the different behaviors by first comparing the input to the
8921 // minimum output value (-2**-63 for truncating to long, -2**-31 for truncating to int).
8922 // If the input is greater than or equal to the minimum, it procedes to the truncate
8923 // instruction, which will handle such an input the same way irrespective of NAN2008.
8924 // Otherwise the input is compared to itself to determine whether it is a NaN or not
8925 // in order to return either zero or the minimum value.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008926 if (result_type == DataType::Type::kInt64) {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008927 if (isR6) {
8928 // trunc.l.s/trunc.l.d requires MIPSR2+ with FR=1. MIPS32R6 is implemented as a secondary
8929 // architecture on top of MIPS64R6, which has FR=1, and therefore can use the instruction.
8930 FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
8931 Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
8932 Register dst_low = locations->Out().AsRegisterPairLow<Register>();
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008933
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008934 if (input_type == DataType::Type::kFloat32) {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008935 __ TruncLS(FTMP, src);
8936 } else {
8937 __ TruncLD(FTMP, src);
8938 }
8939 __ Mfc1(dst_low, FTMP);
8940 __ Mfhc1(dst_high, FTMP);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008941 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008942 QuickEntrypointEnum entrypoint =
8943 (input_type == DataType::Type::kFloat32) ? kQuickF2l : kQuickD2l;
Serban Constantinescufca16662016-07-14 09:21:59 +01008944 codegen_->InvokeRuntime(entrypoint, conversion, conversion->GetDexPc());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008945 if (input_type == DataType::Type::kFloat32) {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008946 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
8947 } else {
8948 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
8949 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008950 }
8951 } else {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008952 FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
8953 Register dst = locations->Out().AsRegister<Register>();
8954 MipsLabel truncate;
8955 MipsLabel done;
8956
Lena Djokicf4e23a82017-05-09 15:43:45 +02008957 if (!isR6) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008958 if (input_type == DataType::Type::kFloat32) {
Lena Djokicf4e23a82017-05-09 15:43:45 +02008959 uint32_t min_val = bit_cast<uint32_t, float>(std::numeric_limits<int32_t>::min());
8960 __ LoadConst32(TMP, min_val);
8961 __ Mtc1(TMP, FTMP);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008962 } else {
Lena Djokicf4e23a82017-05-09 15:43:45 +02008963 uint64_t min_val = bit_cast<uint64_t, double>(std::numeric_limits<int32_t>::min());
8964 __ LoadConst32(TMP, High32Bits(min_val));
8965 __ Mtc1(ZERO, FTMP);
8966 __ MoveToFpuHigh(TMP, FTMP);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008967 }
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008968
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008969 if (input_type == DataType::Type::kFloat32) {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008970 __ ColeS(0, FTMP, src);
8971 } else {
8972 __ ColeD(0, FTMP, src);
8973 }
8974 __ Bc1t(0, &truncate);
8975
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008976 if (input_type == DataType::Type::kFloat32) {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008977 __ CeqS(0, src, src);
8978 } else {
8979 __ CeqD(0, src, src);
8980 }
8981 __ LoadConst32(dst, std::numeric_limits<int32_t>::min());
8982 __ Movf(dst, ZERO, 0);
Lena Djokicf4e23a82017-05-09 15:43:45 +02008983
8984 __ B(&done);
8985
8986 __ Bind(&truncate);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008987 }
8988
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008989 if (input_type == DataType::Type::kFloat32) {
Alexey Frunzebaf60b72015-12-22 15:15:03 -08008990 __ TruncWS(FTMP, src);
8991 } else {
8992 __ TruncWD(FTMP, src);
8993 }
8994 __ Mfc1(dst, FTMP);
8995
Lena Djokicf4e23a82017-05-09 15:43:45 +02008996 if (!isR6) {
8997 __ Bind(&done);
8998 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02008999 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009000 } else if (DataType::IsFloatingPointType(result_type) &&
9001 DataType::IsFloatingPointType(input_type)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009002 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
9003 FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009004 if (result_type == DataType::Type::kFloat32) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009005 __ Cvtsd(dst, src);
9006 } else {
9007 __ Cvtds(dst, src);
9008 }
9009 } else {
9010 LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
9011 << " to " << result_type;
9012 }
9013}
9014
9015void LocationsBuilderMIPS::VisitUShr(HUShr* ushr) {
9016 HandleShift(ushr);
9017}
9018
9019void InstructionCodeGeneratorMIPS::VisitUShr(HUShr* ushr) {
9020 HandleShift(ushr);
9021}
9022
9023void LocationsBuilderMIPS::VisitXor(HXor* instruction) {
9024 HandleBinaryOp(instruction);
9025}
9026
9027void InstructionCodeGeneratorMIPS::VisitXor(HXor* instruction) {
9028 HandleBinaryOp(instruction);
9029}
9030
9031void LocationsBuilderMIPS::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9032 // Nothing to do, this should be removed during prepare for register allocator.
9033 LOG(FATAL) << "Unreachable";
9034}
9035
9036void InstructionCodeGeneratorMIPS::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9037 // Nothing to do, this should be removed during prepare for register allocator.
9038 LOG(FATAL) << "Unreachable";
9039}
9040
9041void LocationsBuilderMIPS::VisitEqual(HEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009042 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009043}
9044
9045void InstructionCodeGeneratorMIPS::VisitEqual(HEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009046 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009047}
9048
9049void LocationsBuilderMIPS::VisitNotEqual(HNotEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009050 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009051}
9052
9053void InstructionCodeGeneratorMIPS::VisitNotEqual(HNotEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009054 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009055}
9056
9057void LocationsBuilderMIPS::VisitLessThan(HLessThan* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009058 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009059}
9060
9061void InstructionCodeGeneratorMIPS::VisitLessThan(HLessThan* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009062 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009063}
9064
9065void LocationsBuilderMIPS::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009066 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009067}
9068
9069void InstructionCodeGeneratorMIPS::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009070 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009071}
9072
9073void LocationsBuilderMIPS::VisitGreaterThan(HGreaterThan* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009074 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009075}
9076
9077void InstructionCodeGeneratorMIPS::VisitGreaterThan(HGreaterThan* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009078 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009079}
9080
9081void LocationsBuilderMIPS::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009082 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009083}
9084
9085void InstructionCodeGeneratorMIPS::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009086 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009087}
9088
9089void LocationsBuilderMIPS::VisitBelow(HBelow* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009090 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009091}
9092
9093void InstructionCodeGeneratorMIPS::VisitBelow(HBelow* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009094 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009095}
9096
9097void LocationsBuilderMIPS::VisitBelowOrEqual(HBelowOrEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009098 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009099}
9100
9101void InstructionCodeGeneratorMIPS::VisitBelowOrEqual(HBelowOrEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009102 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009103}
9104
9105void LocationsBuilderMIPS::VisitAbove(HAbove* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009106 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009107}
9108
9109void InstructionCodeGeneratorMIPS::VisitAbove(HAbove* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009110 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009111}
9112
9113void LocationsBuilderMIPS::VisitAboveOrEqual(HAboveOrEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009114 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009115}
9116
9117void InstructionCodeGeneratorMIPS::VisitAboveOrEqual(HAboveOrEqual* comp) {
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00009118 HandleCondition(comp);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009119}
9120
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009121void LocationsBuilderMIPS::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9122 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009123 new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009124 locations->SetInAt(0, Location::RequiresRegister());
Alexey Frunze3b8c82f2017-10-10 23:01:34 -07009125 if (!codegen_->GetInstructionSetFeatures().IsR6()) {
9126 uint32_t num_entries = switch_instr->GetNumEntries();
9127 if (num_entries > InstructionCodeGeneratorMIPS::kPackedSwitchJumpTableThreshold) {
9128 // When there's no HMipsComputeBaseMethodAddress input, R2 uses the NAL
9129 // instruction to simulate PC-relative addressing when accessing the jump table.
9130 // NAL clobbers RA. Make sure RA is preserved.
9131 codegen_->ClobberRA();
9132 }
9133 }
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009134}
9135
Alexey Frunze96b66822016-09-10 02:32:44 -07009136void InstructionCodeGeneratorMIPS::GenPackedSwitchWithCompares(Register value_reg,
9137 int32_t lower_bound,
9138 uint32_t num_entries,
9139 HBasicBlock* switch_block,
9140 HBasicBlock* default_block) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009141 // Create a set of compare/jumps.
Vladimir Markof3e0ee22015-12-17 15:23:13 +00009142 Register temp_reg = TMP;
9143 __ Addiu32(temp_reg, value_reg, -lower_bound);
9144 // Jump to default if index is negative
9145 // Note: We don't check the case that index is positive while value < lower_bound, because in
9146 // this case, index >= num_entries must be true. So that we can save one branch instruction.
9147 __ Bltz(temp_reg, codegen_->GetLabelOf(default_block));
9148
Alexey Frunze96b66822016-09-10 02:32:44 -07009149 const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
Vladimir Markof3e0ee22015-12-17 15:23:13 +00009150 // Jump to successors[0] if value == lower_bound.
9151 __ Beqz(temp_reg, codegen_->GetLabelOf(successors[0]));
9152 int32_t last_index = 0;
9153 for (; num_entries - last_index > 2; last_index += 2) {
9154 __ Addiu(temp_reg, temp_reg, -2);
9155 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
9156 __ Bltz(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
9157 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
9158 __ Beqz(temp_reg, codegen_->GetLabelOf(successors[last_index + 2]));
9159 }
9160 if (num_entries - last_index == 2) {
9161 // The last missing case_value.
9162 __ Addiu(temp_reg, temp_reg, -1);
9163 __ Beqz(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009164 }
9165
Vladimir Markof3e0ee22015-12-17 15:23:13 +00009166 // And the default for any other value.
Alexey Frunze96b66822016-09-10 02:32:44 -07009167 if (!codegen_->GoesToNextBlock(switch_block, default_block)) {
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009168 __ B(codegen_->GetLabelOf(default_block));
9169 }
9170}
9171
Alexey Frunze96b66822016-09-10 02:32:44 -07009172void InstructionCodeGeneratorMIPS::GenTableBasedPackedSwitch(Register value_reg,
9173 Register constant_area,
9174 int32_t lower_bound,
9175 uint32_t num_entries,
9176 HBasicBlock* switch_block,
9177 HBasicBlock* default_block) {
9178 // Create a jump table.
9179 std::vector<MipsLabel*> labels(num_entries);
9180 const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
9181 for (uint32_t i = 0; i < num_entries; i++) {
9182 labels[i] = codegen_->GetLabelOf(successors[i]);
9183 }
9184 JumpTable* table = __ CreateJumpTable(std::move(labels));
9185
9186 // Is the value in range?
9187 __ Addiu32(TMP, value_reg, -lower_bound);
9188 if (IsInt<16>(static_cast<int32_t>(num_entries))) {
9189 __ Sltiu(AT, TMP, num_entries);
9190 __ Beqz(AT, codegen_->GetLabelOf(default_block));
9191 } else {
9192 __ LoadConst32(AT, num_entries);
9193 __ Bgeu(TMP, AT, codegen_->GetLabelOf(default_block));
9194 }
9195
9196 // We are in the range of the table.
9197 // Load the target address from the jump table, indexing by the value.
9198 __ LoadLabelAddress(AT, constant_area, table->GetLabel());
Chris Larsencd0295d2017-03-31 15:26:54 -07009199 __ ShiftAndAdd(TMP, TMP, AT, 2, TMP);
Alexey Frunze96b66822016-09-10 02:32:44 -07009200 __ Lw(TMP, TMP, 0);
9201 // Compute the absolute target address by adding the table start address
9202 // (the table contains offsets to targets relative to its start).
9203 __ Addu(TMP, TMP, AT);
9204 // And jump.
9205 __ Jr(TMP);
9206 __ NopIfNoReordering();
9207}
9208
9209void InstructionCodeGeneratorMIPS::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9210 int32_t lower_bound = switch_instr->GetStartValue();
9211 uint32_t num_entries = switch_instr->GetNumEntries();
9212 LocationSummary* locations = switch_instr->GetLocations();
9213 Register value_reg = locations->InAt(0).AsRegister<Register>();
9214 HBasicBlock* switch_block = switch_instr->GetBlock();
9215 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
9216
Alexey Frunze3b8c82f2017-10-10 23:01:34 -07009217 if (num_entries > kPackedSwitchJumpTableThreshold) {
Alexey Frunze96b66822016-09-10 02:32:44 -07009218 // R6 uses PC-relative addressing to access the jump table.
Alexey Frunze3b8c82f2017-10-10 23:01:34 -07009219 //
9220 // R2, OTOH, uses an HMipsComputeBaseMethodAddress input (when available)
9221 // to access the jump table and it is implemented by changing HPackedSwitch to
9222 // HMipsPackedSwitch, which bears HMipsComputeBaseMethodAddress (see
9223 // VisitMipsPackedSwitch()).
9224 //
9225 // When there's no HMipsComputeBaseMethodAddress input (e.g. in presence of
9226 // irreducible loops), R2 uses the NAL instruction to simulate PC-relative
9227 // addressing.
Alexey Frunze96b66822016-09-10 02:32:44 -07009228 GenTableBasedPackedSwitch(value_reg,
9229 ZERO,
9230 lower_bound,
9231 num_entries,
9232 switch_block,
9233 default_block);
9234 } else {
9235 GenPackedSwitchWithCompares(value_reg,
9236 lower_bound,
9237 num_entries,
9238 switch_block,
9239 default_block);
9240 }
9241}
9242
9243void LocationsBuilderMIPS::VisitMipsPackedSwitch(HMipsPackedSwitch* switch_instr) {
9244 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009245 new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
Alexey Frunze96b66822016-09-10 02:32:44 -07009246 locations->SetInAt(0, Location::RequiresRegister());
9247 // Constant area pointer (HMipsComputeBaseMethodAddress).
9248 locations->SetInAt(1, Location::RequiresRegister());
9249}
9250
9251void InstructionCodeGeneratorMIPS::VisitMipsPackedSwitch(HMipsPackedSwitch* switch_instr) {
9252 int32_t lower_bound = switch_instr->GetStartValue();
9253 uint32_t num_entries = switch_instr->GetNumEntries();
9254 LocationSummary* locations = switch_instr->GetLocations();
9255 Register value_reg = locations->InAt(0).AsRegister<Register>();
9256 Register constant_area = locations->InAt(1).AsRegister<Register>();
9257 HBasicBlock* switch_block = switch_instr->GetBlock();
9258 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
9259
9260 // This is an R2-only path. HPackedSwitch has been changed to
9261 // HMipsPackedSwitch, which bears HMipsComputeBaseMethodAddress
9262 // required to address the jump table relative to PC.
9263 GenTableBasedPackedSwitch(value_reg,
9264 constant_area,
9265 lower_bound,
9266 num_entries,
9267 switch_block,
9268 default_block);
9269}
9270
Alexey Frunzee3fb2452016-05-10 16:08:05 -07009271void LocationsBuilderMIPS::VisitMipsComputeBaseMethodAddress(
9272 HMipsComputeBaseMethodAddress* insn) {
9273 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009274 new (GetGraph()->GetAllocator()) LocationSummary(insn, LocationSummary::kNoCall);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07009275 locations->SetOut(Location::RequiresRegister());
9276}
9277
9278void InstructionCodeGeneratorMIPS::VisitMipsComputeBaseMethodAddress(
9279 HMipsComputeBaseMethodAddress* insn) {
9280 LocationSummary* locations = insn->GetLocations();
9281 Register reg = locations->Out().AsRegister<Register>();
9282
9283 CHECK(!codegen_->GetInstructionSetFeatures().IsR6());
9284
9285 // Generate a dummy PC-relative call to obtain PC.
9286 __ Nal();
9287 // Grab the return address off RA.
9288 __ Move(reg, RA);
9289
9290 // Remember this offset (the obtained PC value) for later use with constant area.
9291 __ BindPcRelBaseLabel();
9292}
9293
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009294void LocationsBuilderMIPS::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
9295 // The trampoline uses the same calling convention as dex calling conventions,
9296 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
9297 // the method_idx.
9298 HandleInvoke(invoke);
9299}
9300
9301void InstructionCodeGeneratorMIPS::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
9302 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
9303}
9304
Roland Levillain2aba7cd2016-02-03 12:27:20 +00009305void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet* instruction) {
9306 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009307 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Roland Levillain2aba7cd2016-02-03 12:27:20 +00009308 locations->SetInAt(0, Location::RequiresRegister());
9309 locations->SetOut(Location::RequiresRegister());
Nicolas Geoffraya42363f2015-12-17 14:57:09 +00009310}
9311
Roland Levillain2aba7cd2016-02-03 12:27:20 +00009312void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet* instruction) {
9313 LocationSummary* locations = instruction->GetLocations();
Vladimir Markoa1de9182016-02-25 11:37:38 +00009314 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
Nicolas Geoffrayff484b92016-07-13 14:13:48 +01009315 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
Roland Levillain2aba7cd2016-02-03 12:27:20 +00009316 instruction->GetIndex(), kMipsPointerSize).SizeValue();
Nicolas Geoffrayff484b92016-07-13 14:13:48 +01009317 __ LoadFromOffset(kLoadWord,
9318 locations->Out().AsRegister<Register>(),
9319 locations->InAt(0).AsRegister<Register>(),
9320 method_offset);
Roland Levillain2aba7cd2016-02-03 12:27:20 +00009321 } else {
Nicolas Geoffrayff484b92016-07-13 14:13:48 +01009322 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
Matthew Gharrity465ecc82016-07-19 21:32:52 +00009323 instruction->GetIndex(), kMipsPointerSize));
Artem Udovichenkoa62cb9b2016-06-30 09:18:25 +00009324 __ LoadFromOffset(kLoadWord,
9325 locations->Out().AsRegister<Register>(),
9326 locations->InAt(0).AsRegister<Register>(),
9327 mirror::Class::ImtPtrOffset(kMipsPointerSize).Uint32Value());
Nicolas Geoffrayff484b92016-07-13 14:13:48 +01009328 __ LoadFromOffset(kLoadWord,
9329 locations->Out().AsRegister<Register>(),
9330 locations->Out().AsRegister<Register>(),
9331 method_offset);
Roland Levillain2aba7cd2016-02-03 12:27:20 +00009332 }
Nicolas Geoffraya42363f2015-12-17 14:57:09 +00009333}
9334
xueliang.zhonge0eb4832017-10-30 13:43:14 +00009335void LocationsBuilderMIPS::VisitIntermediateAddress(HIntermediateAddress* instruction
9336 ATTRIBUTE_UNUSED) {
9337 LOG(FATAL) << "Unreachable";
9338}
9339
9340void InstructionCodeGeneratorMIPS::VisitIntermediateAddress(HIntermediateAddress* instruction
9341 ATTRIBUTE_UNUSED) {
9342 LOG(FATAL) << "Unreachable";
9343}
9344
Goran Jakovljevicf652cec2015-08-25 16:11:42 +02009345#undef __
9346#undef QUICK_ENTRY_POINT
9347
9348} // namespace mips
9349} // namespace art