blob: 6d6ff7576bef2e56e559c50b9b4407cc370a948b [file] [log] [blame]
Chris Larsen701566a2015-10-27 15:29:13 -07001/*
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 "intrinsics_mips.h"
18
19#include "arch/mips/instruction_set_features_mips.h"
20#include "art_method.h"
21#include "code_generator_mips.h"
22#include "entrypoints/quick/quick_entrypoints.h"
Andreas Gampe09659c22017-09-18 18:23:32 -070023#include "heap_poisoning.h"
Chris Larsen701566a2015-10-27 15:29:13 -070024#include "intrinsics.h"
25#include "mirror/array-inl.h"
Andreas Gampe895f9222017-07-05 09:53:32 -070026#include "mirror/object_array-inl.h"
Chris Larsen701566a2015-10-27 15:29:13 -070027#include "mirror/string.h"
Andreas Gampe508fdf32017-06-05 16:42:13 -070028#include "scoped_thread_state_change-inl.h"
Chris Larsen701566a2015-10-27 15:29:13 -070029#include "thread.h"
30#include "utils/mips/assembler_mips.h"
31#include "utils/mips/constants_mips.h"
32
33namespace art {
34
35namespace mips {
36
37IntrinsicLocationsBuilderMIPS::IntrinsicLocationsBuilderMIPS(CodeGeneratorMIPS* codegen)
Vladimir Markoca6fff82017-10-03 14:49:14 +010038 : codegen_(codegen), allocator_(codegen->GetGraph()->GetAllocator()) {
Chris Larsen701566a2015-10-27 15:29:13 -070039}
40
41MipsAssembler* IntrinsicCodeGeneratorMIPS::GetAssembler() {
42 return reinterpret_cast<MipsAssembler*>(codegen_->GetAssembler());
43}
44
45ArenaAllocator* IntrinsicCodeGeneratorMIPS::GetAllocator() {
Vladimir Markoca6fff82017-10-03 14:49:14 +010046 return codegen_->GetGraph()->GetAllocator();
Chris Larsen701566a2015-10-27 15:29:13 -070047}
48
Alexey Frunzebb9863a2016-01-11 15:51:16 -080049inline bool IntrinsicCodeGeneratorMIPS::IsR2OrNewer() const {
Chris Larsene16ce5a2015-11-18 12:30:20 -080050 return codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
51}
52
Alexey Frunzebb9863a2016-01-11 15:51:16 -080053inline bool IntrinsicCodeGeneratorMIPS::IsR6() const {
Chris Larsene16ce5a2015-11-18 12:30:20 -080054 return codegen_->GetInstructionSetFeatures().IsR6();
55}
56
Alexey Frunzebb9863a2016-01-11 15:51:16 -080057inline bool IntrinsicCodeGeneratorMIPS::Is32BitFPU() const {
58 return codegen_->GetInstructionSetFeatures().Is32BitFloatingPoint();
59}
60
Chris Larsen701566a2015-10-27 15:29:13 -070061#define __ codegen->GetAssembler()->
62
63static void MoveFromReturnRegister(Location trg,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010064 DataType::Type type,
Chris Larsen701566a2015-10-27 15:29:13 -070065 CodeGeneratorMIPS* codegen) {
66 if (!trg.IsValid()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010067 DCHECK_EQ(type, DataType::Type::kVoid);
Chris Larsen701566a2015-10-27 15:29:13 -070068 return;
69 }
70
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010071 DCHECK_NE(type, DataType::Type::kVoid);
Chris Larsen701566a2015-10-27 15:29:13 -070072
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010073 if (DataType::IsIntegralType(type) || type == DataType::Type::kReference) {
Chris Larsen701566a2015-10-27 15:29:13 -070074 Register trg_reg = trg.AsRegister<Register>();
75 if (trg_reg != V0) {
76 __ Move(V0, trg_reg);
77 }
78 } else {
79 FRegister trg_reg = trg.AsFpuRegister<FRegister>();
80 if (trg_reg != F0) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010081 if (type == DataType::Type::kFloat32) {
Chris Larsen701566a2015-10-27 15:29:13 -070082 __ MovS(F0, trg_reg);
83 } else {
84 __ MovD(F0, trg_reg);
85 }
86 }
87 }
88}
89
90static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS* codegen) {
91 InvokeDexCallingConventionVisitorMIPS calling_convention_visitor;
92 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
93}
94
95// Slow-path for fallback (calling the managed code to handle the
96// intrinsic) in an intrinsified call. This will copy the arguments
97// into the positions for a regular call.
98//
99// Note: The actual parameters are required to be in the locations
100// given by the invoke's location summary. If an intrinsic
101// modifies those locations before a slowpath call, they must be
102// restored!
103class IntrinsicSlowPathMIPS : public SlowPathCodeMIPS {
104 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000105 explicit IntrinsicSlowPathMIPS(HInvoke* invoke) : SlowPathCodeMIPS(invoke), invoke_(invoke) { }
Chris Larsen701566a2015-10-27 15:29:13 -0700106
107 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
108 CodeGeneratorMIPS* codegen = down_cast<CodeGeneratorMIPS*>(codegen_in);
109
110 __ Bind(GetEntryLabel());
111
112 SaveLiveRegisters(codegen, invoke_->GetLocations());
113
114 MoveArguments(invoke_, codegen);
115
116 if (invoke_->IsInvokeStaticOrDirect()) {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100117 codegen->GenerateStaticOrDirectCall(
118 invoke_->AsInvokeStaticOrDirect(), Location::RegisterLocation(A0), this);
Chris Larsen701566a2015-10-27 15:29:13 -0700119 } else {
Vladimir Markoe7197bf2017-06-02 17:00:23 +0100120 codegen->GenerateVirtualCall(
121 invoke_->AsInvokeVirtual(), Location::RegisterLocation(A0), this);
Chris Larsen701566a2015-10-27 15:29:13 -0700122 }
123
124 // Copy the result back to the expected output.
125 Location out = invoke_->GetLocations()->Out();
126 if (out.IsValid()) {
127 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
128 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
129 MoveFromReturnRegister(out, invoke_->GetType(), codegen);
130 }
131
132 RestoreLiveRegisters(codegen, invoke_->GetLocations());
133 __ B(GetExitLabel());
134 }
135
136 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathMIPS"; }
137
138 private:
139 // The instruction where this slow path is happening.
140 HInvoke* const invoke_;
141
142 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathMIPS);
143};
144
145#undef __
146
147bool IntrinsicLocationsBuilderMIPS::TryDispatch(HInvoke* invoke) {
148 Dispatch(invoke);
149 LocationSummary* res = invoke->GetLocations();
150 return res != nullptr && res->Intrinsified();
151}
152
153#define __ assembler->
154
Vladimir Markoca6fff82017-10-03 14:49:14 +0100155static void CreateFPToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
156 LocationSummary* locations =
157 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700158 locations->SetInAt(0, Location::RequiresFpuRegister());
159 locations->SetOut(Location::RequiresRegister());
160}
161
162static void MoveFPToInt(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
163 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
164
165 if (is64bit) {
166 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
167 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
168
169 __ Mfc1(out_lo, in);
Alexey Frunzebb9863a2016-01-11 15:51:16 -0800170 __ MoveFromFpuHigh(out_hi, in);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700171 } else {
172 Register out = locations->Out().AsRegister<Register>();
173
174 __ Mfc1(out, in);
175 }
176}
177
178// long java.lang.Double.doubleToRawLongBits(double)
179void IntrinsicLocationsBuilderMIPS::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100180 CreateFPToIntLocations(allocator_, invoke);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700181}
182
183void IntrinsicCodeGeneratorMIPS::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000184 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700185}
186
187// int java.lang.Float.floatToRawIntBits(float)
188void IntrinsicLocationsBuilderMIPS::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100189 CreateFPToIntLocations(allocator_, invoke);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700190}
191
192void IntrinsicCodeGeneratorMIPS::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000193 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700194}
195
Vladimir Markoca6fff82017-10-03 14:49:14 +0100196static void CreateIntToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
197 LocationSummary* locations =
198 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700199 locations->SetInAt(0, Location::RequiresRegister());
200 locations->SetOut(Location::RequiresFpuRegister());
201}
202
203static void MoveIntToFP(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
204 FRegister out = locations->Out().AsFpuRegister<FRegister>();
205
206 if (is64bit) {
207 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
208 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
209
210 __ Mtc1(in_lo, out);
Alexey Frunzebb9863a2016-01-11 15:51:16 -0800211 __ MoveToFpuHigh(in_hi, out);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700212 } else {
213 Register in = locations->InAt(0).AsRegister<Register>();
214
215 __ Mtc1(in, out);
216 }
217}
218
219// double java.lang.Double.longBitsToDouble(long)
220void IntrinsicLocationsBuilderMIPS::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100221 CreateIntToFPLocations(allocator_, invoke);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700222}
223
224void IntrinsicCodeGeneratorMIPS::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000225 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700226}
227
228// float java.lang.Float.intBitsToFloat(int)
229void IntrinsicLocationsBuilderMIPS::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100230 CreateIntToFPLocations(allocator_, invoke);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700231}
232
233void IntrinsicCodeGeneratorMIPS::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000234 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700235}
236
Vladimir Markoca6fff82017-10-03 14:49:14 +0100237static void CreateIntToIntLocations(ArenaAllocator* allocator,
Chris Larsen86829602015-11-18 12:27:52 -0800238 HInvoke* invoke,
239 Location::OutputOverlap overlaps = Location::kNoOutputOverlap) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100240 LocationSummary* locations =
241 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700242 locations->SetInAt(0, Location::RequiresRegister());
Chris Larsen86829602015-11-18 12:27:52 -0800243 locations->SetOut(Location::RequiresRegister(), overlaps);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700244}
245
Chris Larsen70014c82015-11-18 12:26:08 -0800246static void GenReverse(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100247 DataType::Type type,
Chris Larsen70014c82015-11-18 12:26:08 -0800248 bool isR2OrNewer,
249 bool isR6,
250 bool reverseBits,
251 MipsAssembler* assembler) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100252 DCHECK(type == DataType::Type::kInt16 ||
253 type == DataType::Type::kInt32 ||
254 type == DataType::Type::kInt64);
255 DCHECK(type != DataType::Type::kInt16 || !reverseBits);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700256
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100257 if (type == DataType::Type::kInt16) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700258 Register in = locations->InAt(0).AsRegister<Register>();
259 Register out = locations->Out().AsRegister<Register>();
260
261 if (isR2OrNewer) {
262 __ Wsbh(out, in);
263 __ Seh(out, out);
264 } else {
265 __ Sll(TMP, in, 24);
266 __ Sra(TMP, TMP, 16);
267 __ Sll(out, in, 16);
268 __ Srl(out, out, 24);
269 __ Or(out, out, TMP);
270 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100271 } else if (type == DataType::Type::kInt32) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700272 Register in = locations->InAt(0).AsRegister<Register>();
273 Register out = locations->Out().AsRegister<Register>();
274
275 if (isR2OrNewer) {
276 __ Rotr(out, in, 16);
277 __ Wsbh(out, out);
278 } else {
279 // MIPS32r1
280 // __ Rotr(out, in, 16);
281 __ Sll(TMP, in, 16);
282 __ Srl(out, in, 16);
283 __ Or(out, out, TMP);
284 // __ Wsbh(out, out);
285 __ LoadConst32(AT, 0x00FF00FF);
286 __ And(TMP, out, AT);
287 __ Sll(TMP, TMP, 8);
288 __ Srl(out, out, 8);
289 __ And(out, out, AT);
290 __ Or(out, out, TMP);
291 }
Chris Larsen70014c82015-11-18 12:26:08 -0800292 if (reverseBits) {
293 if (isR6) {
294 __ Bitswap(out, out);
295 } else {
296 __ LoadConst32(AT, 0x0F0F0F0F);
297 __ And(TMP, out, AT);
298 __ Sll(TMP, TMP, 4);
299 __ Srl(out, out, 4);
300 __ And(out, out, AT);
301 __ Or(out, TMP, out);
302 __ LoadConst32(AT, 0x33333333);
303 __ And(TMP, out, AT);
304 __ Sll(TMP, TMP, 2);
305 __ Srl(out, out, 2);
306 __ And(out, out, AT);
307 __ Or(out, TMP, out);
308 __ LoadConst32(AT, 0x55555555);
309 __ And(TMP, out, AT);
310 __ Sll(TMP, TMP, 1);
311 __ Srl(out, out, 1);
312 __ And(out, out, AT);
313 __ Or(out, TMP, out);
314 }
315 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100316 } else if (type == DataType::Type::kInt64) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700317 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
318 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
319 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
320 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
321
322 if (isR2OrNewer) {
323 __ Rotr(AT, in_hi, 16);
324 __ Rotr(TMP, in_lo, 16);
325 __ Wsbh(out_lo, AT);
326 __ Wsbh(out_hi, TMP);
327 } else {
328 // When calling CreateIntToIntLocations() we promised that the
329 // use of the out_lo/out_hi wouldn't overlap with the use of
330 // in_lo/in_hi. Be very careful not to write to out_lo/out_hi
331 // until we're completely done reading from in_lo/in_hi.
332 // __ Rotr(TMP, in_lo, 16);
333 __ Sll(TMP, in_lo, 16);
334 __ Srl(AT, in_lo, 16);
335 __ Or(TMP, TMP, AT); // Hold in TMP until it's safe
336 // to write to out_hi.
337 // __ Rotr(out_lo, in_hi, 16);
338 __ Sll(AT, in_hi, 16);
339 __ Srl(out_lo, in_hi, 16); // Here we are finally done reading
340 // from in_lo/in_hi so it's okay to
341 // write to out_lo/out_hi.
342 __ Or(out_lo, out_lo, AT);
343 // __ Wsbh(out_hi, out_hi);
344 __ LoadConst32(AT, 0x00FF00FF);
345 __ And(out_hi, TMP, AT);
346 __ Sll(out_hi, out_hi, 8);
347 __ Srl(TMP, TMP, 8);
348 __ And(TMP, TMP, AT);
349 __ Or(out_hi, out_hi, TMP);
350 // __ Wsbh(out_lo, out_lo);
351 __ And(TMP, out_lo, AT); // AT already holds the correct mask value
352 __ Sll(TMP, TMP, 8);
353 __ Srl(out_lo, out_lo, 8);
354 __ And(out_lo, out_lo, AT);
355 __ Or(out_lo, out_lo, TMP);
356 }
Chris Larsen70014c82015-11-18 12:26:08 -0800357 if (reverseBits) {
358 if (isR6) {
359 __ Bitswap(out_hi, out_hi);
360 __ Bitswap(out_lo, out_lo);
361 } else {
362 __ LoadConst32(AT, 0x0F0F0F0F);
363 __ And(TMP, out_hi, AT);
364 __ Sll(TMP, TMP, 4);
365 __ Srl(out_hi, out_hi, 4);
366 __ And(out_hi, out_hi, AT);
367 __ Or(out_hi, TMP, out_hi);
368 __ And(TMP, out_lo, AT);
369 __ Sll(TMP, TMP, 4);
370 __ Srl(out_lo, out_lo, 4);
371 __ And(out_lo, out_lo, AT);
372 __ Or(out_lo, TMP, out_lo);
373 __ LoadConst32(AT, 0x33333333);
374 __ And(TMP, out_hi, AT);
375 __ Sll(TMP, TMP, 2);
376 __ Srl(out_hi, out_hi, 2);
377 __ And(out_hi, out_hi, AT);
378 __ Or(out_hi, TMP, out_hi);
379 __ And(TMP, out_lo, AT);
380 __ Sll(TMP, TMP, 2);
381 __ Srl(out_lo, out_lo, 2);
382 __ And(out_lo, out_lo, AT);
383 __ Or(out_lo, TMP, out_lo);
384 __ LoadConst32(AT, 0x55555555);
385 __ And(TMP, out_hi, AT);
386 __ Sll(TMP, TMP, 1);
387 __ Srl(out_hi, out_hi, 1);
388 __ And(out_hi, out_hi, AT);
389 __ Or(out_hi, TMP, out_hi);
390 __ And(TMP, out_lo, AT);
391 __ Sll(TMP, TMP, 1);
392 __ Srl(out_lo, out_lo, 1);
393 __ And(out_lo, out_lo, AT);
394 __ Or(out_lo, TMP, out_lo);
395 }
396 }
Chris Larsen3f8bf652015-10-28 10:08:56 -0700397 }
398}
399
400// int java.lang.Integer.reverseBytes(int)
401void IntrinsicLocationsBuilderMIPS::VisitIntegerReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100402 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700403}
404
405void IntrinsicCodeGeneratorMIPS::VisitIntegerReverseBytes(HInvoke* invoke) {
Chris Larsen70014c82015-11-18 12:26:08 -0800406 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100407 DataType::Type::kInt32,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800408 IsR2OrNewer(),
409 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800410 /* reverseBits */ false,
Chris Larsen70014c82015-11-18 12:26:08 -0800411 GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700412}
413
414// long java.lang.Long.reverseBytes(long)
415void IntrinsicLocationsBuilderMIPS::VisitLongReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100416 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700417}
418
419void IntrinsicCodeGeneratorMIPS::VisitLongReverseBytes(HInvoke* invoke) {
Chris Larsen70014c82015-11-18 12:26:08 -0800420 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100421 DataType::Type::kInt64,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800422 IsR2OrNewer(),
423 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800424 /* reverseBits */ false,
Chris Larsen70014c82015-11-18 12:26:08 -0800425 GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700426}
427
428// short java.lang.Short.reverseBytes(short)
429void IntrinsicLocationsBuilderMIPS::VisitShortReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100430 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700431}
432
433void IntrinsicCodeGeneratorMIPS::VisitShortReverseBytes(HInvoke* invoke) {
Chris Larsen70014c82015-11-18 12:26:08 -0800434 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100435 DataType::Type::kInt16,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800436 IsR2OrNewer(),
437 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800438 /* reverseBits */ false,
Chris Larsen70014c82015-11-18 12:26:08 -0800439 GetAssembler());
440}
441
Chris Larsene3845472015-11-18 12:27:15 -0800442static void GenNumberOfLeadingZeroes(LocationSummary* locations,
443 bool is64bit,
444 bool isR6,
445 MipsAssembler* assembler) {
446 Register out = locations->Out().AsRegister<Register>();
447 if (is64bit) {
448 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
449 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
450
451 if (isR6) {
452 __ ClzR6(AT, in_hi);
453 __ ClzR6(TMP, in_lo);
454 __ Seleqz(TMP, TMP, in_hi);
455 } else {
456 __ ClzR2(AT, in_hi);
457 __ ClzR2(TMP, in_lo);
458 __ Movn(TMP, ZERO, in_hi);
459 }
460 __ Addu(out, AT, TMP);
461 } else {
462 Register in = locations->InAt(0).AsRegister<Register>();
463
464 if (isR6) {
465 __ ClzR6(out, in);
466 } else {
467 __ ClzR2(out, in);
468 }
469 }
470}
471
472// int java.lang.Integer.numberOfLeadingZeros(int i)
473void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100474 CreateIntToIntLocations(allocator_, invoke);
Chris Larsene3845472015-11-18 12:27:15 -0800475}
476
477void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800478 GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ false, IsR6(), GetAssembler());
Chris Larsene3845472015-11-18 12:27:15 -0800479}
480
481// int java.lang.Long.numberOfLeadingZeros(long i)
482void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100483 CreateIntToIntLocations(allocator_, invoke);
Chris Larsene3845472015-11-18 12:27:15 -0800484}
485
486void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800487 GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ true, IsR6(), GetAssembler());
Chris Larsene3845472015-11-18 12:27:15 -0800488}
489
Chris Larsen86829602015-11-18 12:27:52 -0800490static void GenNumberOfTrailingZeroes(LocationSummary* locations,
491 bool is64bit,
492 bool isR6,
Chris Larsen86829602015-11-18 12:27:52 -0800493 MipsAssembler* assembler) {
494 Register out = locations->Out().AsRegister<Register>();
495 Register in_lo;
496 Register in;
497
498 if (is64bit) {
Chris Larsen86829602015-11-18 12:27:52 -0800499 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
500
501 in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
502
503 // If in_lo is zero then count the number of trailing zeroes in in_hi;
504 // otherwise count the number of trailing zeroes in in_lo.
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800505 // out = in_lo ? in_lo : in_hi;
Chris Larsen86829602015-11-18 12:27:52 -0800506 if (isR6) {
507 __ Seleqz(out, in_hi, in_lo);
508 __ Selnez(TMP, in_lo, in_lo);
509 __ Or(out, out, TMP);
510 } else {
511 __ Movz(out, in_hi, in_lo);
512 __ Movn(out, in_lo, in_lo);
513 }
514
515 in = out;
516 } else {
517 in = locations->InAt(0).AsRegister<Register>();
518 // Give in_lo a dummy value to keep the compiler from complaining.
519 // Since we only get here in the 32-bit case, this value will never
520 // be used.
521 in_lo = in;
522 }
523
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800524 if (isR6) {
525 // We don't have an instruction to count the number of trailing zeroes.
526 // Start by flipping the bits end-for-end so we can count the number of
527 // leading zeroes instead.
Chris Larsen86829602015-11-18 12:27:52 -0800528 __ Rotr(out, in, 16);
529 __ Wsbh(out, out);
Chris Larsen86829602015-11-18 12:27:52 -0800530 __ Bitswap(out, out);
531 __ ClzR6(out, out);
532 } else {
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800533 // Convert trailing zeroes to trailing ones, and bits to their left
534 // to zeroes.
535 __ Addiu(TMP, in, -1);
536 __ Xor(out, TMP, in);
537 __ And(out, out, TMP);
538 // Count number of leading zeroes.
Chris Larsen86829602015-11-18 12:27:52 -0800539 __ ClzR2(out, out);
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800540 // Subtract number of leading zeroes from 32 to get number of trailing ones.
541 // Remember that the trailing ones were formerly trailing zeroes.
542 __ LoadConst32(TMP, 32);
543 __ Subu(out, TMP, out);
Chris Larsen86829602015-11-18 12:27:52 -0800544 }
545
546 if (is64bit) {
547 // If in_lo is zero, then we counted the number of trailing zeroes in in_hi so we must add the
548 // number of trailing zeroes in in_lo (32) to get the correct final count
549 __ LoadConst32(TMP, 32);
550 if (isR6) {
551 __ Seleqz(TMP, TMP, in_lo);
552 } else {
553 __ Movn(TMP, ZERO, in_lo);
554 }
555 __ Addu(out, out, TMP);
556 }
557}
558
559// int java.lang.Integer.numberOfTrailingZeros(int i)
560void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100561 CreateIntToIntLocations(allocator_, invoke, Location::kOutputOverlap);
Chris Larsen86829602015-11-18 12:27:52 -0800562}
563
564void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800565 GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ false, IsR6(), GetAssembler());
Chris Larsen86829602015-11-18 12:27:52 -0800566}
567
568// int java.lang.Long.numberOfTrailingZeros(long i)
569void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100570 CreateIntToIntLocations(allocator_, invoke, Location::kOutputOverlap);
Chris Larsen86829602015-11-18 12:27:52 -0800571}
572
573void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800574 GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ true, IsR6(), GetAssembler());
Chris Larsene16ce5a2015-11-18 12:30:20 -0800575}
576
Chris Larsen70014c82015-11-18 12:26:08 -0800577// int java.lang.Integer.reverse(int)
578void IntrinsicLocationsBuilderMIPS::VisitIntegerReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100579 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen70014c82015-11-18 12:26:08 -0800580}
581
582void IntrinsicCodeGeneratorMIPS::VisitIntegerReverse(HInvoke* invoke) {
583 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100584 DataType::Type::kInt32,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800585 IsR2OrNewer(),
586 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800587 /* reverseBits */ true,
Chris Larsen70014c82015-11-18 12:26:08 -0800588 GetAssembler());
589}
590
591// long java.lang.Long.reverse(long)
592void IntrinsicLocationsBuilderMIPS::VisitLongReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100593 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen70014c82015-11-18 12:26:08 -0800594}
595
596void IntrinsicCodeGeneratorMIPS::VisitLongReverse(HInvoke* invoke) {
597 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100598 DataType::Type::kInt64,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800599 IsR2OrNewer(),
600 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800601 /* reverseBits */ true,
Chris Larsen70014c82015-11-18 12:26:08 -0800602 GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700603}
604
Vladimir Markoca6fff82017-10-03 14:49:14 +0100605static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
606 LocationSummary* locations =
607 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsenb74353a2015-11-20 09:07:09 -0800608 locations->SetInAt(0, Location::RequiresFpuRegister());
609 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
610}
611
Chris Larsenedc16452016-02-12 17:59:00 -0800612static void GenBitCount(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100613 DataType::Type type,
Chris Larsenedc16452016-02-12 17:59:00 -0800614 bool isR6,
615 MipsAssembler* assembler) {
Chris Larsenedc16452016-02-12 17:59:00 -0800616 Register out = locations->Out().AsRegister<Register>();
617
618 // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
619 //
620 // A generalization of the best bit counting method to integers of
621 // bit-widths up to 128 (parameterized by type T) is this:
622 //
623 // v = v - ((v >> 1) & (T)~(T)0/3); // temp
624 // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
625 // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
626 // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; // count
627 //
628 // For comparison, for 32-bit quantities, this algorithm can be executed
629 // using 20 MIPS instructions (the calls to LoadConst32() generate two
630 // machine instructions each for the values being used in this algorithm).
631 // A(n unrolled) loop-based algorithm required 25 instructions.
632 //
633 // For 64-bit quantities, this algorithm gets executed twice, (once
634 // for in_lo, and again for in_hi), but saves a few instructions
635 // because the mask values only have to be loaded once. Using this
Chris Larsen8ca4f972016-04-14 16:16:29 -0700636 // algorithm the count for a 64-bit operand can be performed in 29
Chris Larsenedc16452016-02-12 17:59:00 -0800637 // instructions compared to a loop-based algorithm which required 47
638 // instructions.
639
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100640 if (type == DataType::Type::kInt32) {
Chris Larsenedc16452016-02-12 17:59:00 -0800641 Register in = locations->InAt(0).AsRegister<Register>();
642
643 __ Srl(TMP, in, 1);
644 __ LoadConst32(AT, 0x55555555);
645 __ And(TMP, TMP, AT);
646 __ Subu(TMP, in, TMP);
647 __ LoadConst32(AT, 0x33333333);
648 __ And(out, TMP, AT);
649 __ Srl(TMP, TMP, 2);
650 __ And(TMP, TMP, AT);
651 __ Addu(TMP, out, TMP);
652 __ Srl(out, TMP, 4);
653 __ Addu(out, out, TMP);
654 __ LoadConst32(AT, 0x0F0F0F0F);
655 __ And(out, out, AT);
656 __ LoadConst32(TMP, 0x01010101);
657 if (isR6) {
658 __ MulR6(out, out, TMP);
659 } else {
660 __ MulR2(out, out, TMP);
661 }
662 __ Srl(out, out, 24);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100663 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100664 DCHECK_EQ(type, DataType::Type::kInt64);
Chris Larsenedc16452016-02-12 17:59:00 -0800665 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
666 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
667 Register tmp_hi = locations->GetTemp(0).AsRegister<Register>();
668 Register out_hi = locations->GetTemp(1).AsRegister<Register>();
669 Register tmp_lo = TMP;
670 Register out_lo = out;
671
672 __ Srl(tmp_lo, in_lo, 1);
673 __ Srl(tmp_hi, in_hi, 1);
674
675 __ LoadConst32(AT, 0x55555555);
676
677 __ And(tmp_lo, tmp_lo, AT);
678 __ Subu(tmp_lo, in_lo, tmp_lo);
679
680 __ And(tmp_hi, tmp_hi, AT);
681 __ Subu(tmp_hi, in_hi, tmp_hi);
682
683 __ LoadConst32(AT, 0x33333333);
684
685 __ And(out_lo, tmp_lo, AT);
686 __ Srl(tmp_lo, tmp_lo, 2);
687 __ And(tmp_lo, tmp_lo, AT);
688 __ Addu(tmp_lo, out_lo, tmp_lo);
Chris Larsenedc16452016-02-12 17:59:00 -0800689
690 __ And(out_hi, tmp_hi, AT);
691 __ Srl(tmp_hi, tmp_hi, 2);
692 __ And(tmp_hi, tmp_hi, AT);
693 __ Addu(tmp_hi, out_hi, tmp_hi);
Chris Larsenedc16452016-02-12 17:59:00 -0800694
Chris Larsen8ca4f972016-04-14 16:16:29 -0700695 // Here we deviate from the original algorithm a bit. We've reached
696 // the stage where the bitfields holding the subtotals are large
697 // enough to hold the combined subtotals for both the low word, and
698 // the high word. This means that we can add the subtotals for the
699 // the high, and low words into a single word, and compute the final
700 // result for both the high, and low words using fewer instructions.
Chris Larsenedc16452016-02-12 17:59:00 -0800701 __ LoadConst32(AT, 0x0F0F0F0F);
702
Chris Larsen8ca4f972016-04-14 16:16:29 -0700703 __ Addu(TMP, tmp_hi, tmp_lo);
704
705 __ Srl(out, TMP, 4);
706 __ And(out, out, AT);
707 __ And(TMP, TMP, AT);
708 __ Addu(out, out, TMP);
Chris Larsenedc16452016-02-12 17:59:00 -0800709
710 __ LoadConst32(AT, 0x01010101);
711
712 if (isR6) {
Chris Larsen8ca4f972016-04-14 16:16:29 -0700713 __ MulR6(out, out, AT);
Chris Larsenedc16452016-02-12 17:59:00 -0800714 } else {
Chris Larsen8ca4f972016-04-14 16:16:29 -0700715 __ MulR2(out, out, AT);
Chris Larsenedc16452016-02-12 17:59:00 -0800716 }
717
Chris Larsen8ca4f972016-04-14 16:16:29 -0700718 __ Srl(out, out, 24);
Chris Larsenedc16452016-02-12 17:59:00 -0800719 }
720}
721
722// int java.lang.Integer.bitCount(int)
723void IntrinsicLocationsBuilderMIPS::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100724 CreateIntToIntLocations(allocator_, invoke);
Chris Larsenedc16452016-02-12 17:59:00 -0800725}
726
727void IntrinsicCodeGeneratorMIPS::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100728 GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), GetAssembler());
Chris Larsenedc16452016-02-12 17:59:00 -0800729}
730
731// int java.lang.Long.bitCount(int)
732void IntrinsicLocationsBuilderMIPS::VisitLongBitCount(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100733 LocationSummary* locations =
734 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsenedc16452016-02-12 17:59:00 -0800735 locations->SetInAt(0, Location::RequiresRegister());
736 locations->SetOut(Location::RequiresRegister());
737 locations->AddTemp(Location::RequiresRegister());
738 locations->AddTemp(Location::RequiresRegister());
739}
740
741void IntrinsicCodeGeneratorMIPS::VisitLongBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100742 GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler());
Chris Larsenedc16452016-02-12 17:59:00 -0800743}
744
Chris Larsenb74353a2015-11-20 09:07:09 -0800745static void GenMinMaxFP(LocationSummary* locations,
746 bool is_min,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100747 DataType::Type type,
Chris Larsenb74353a2015-11-20 09:07:09 -0800748 bool is_R6,
749 MipsAssembler* assembler) {
750 FRegister out = locations->Out().AsFpuRegister<FRegister>();
751 FRegister a = locations->InAt(0).AsFpuRegister<FRegister>();
752 FRegister b = locations->InAt(1).AsFpuRegister<FRegister>();
753
754 if (is_R6) {
755 MipsLabel noNaNs;
756 MipsLabel done;
757 FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
758
759 // When Java computes min/max it prefers a NaN to a number; the
760 // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
761 // the inputs is a NaN and the other is a valid number, the MIPS
762 // instruction will return the number; Java wants the NaN value
763 // returned. This is why there is extra logic preceding the use of
764 // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
765 // NaN, return the NaN, otherwise return the min/max.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100766 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800767 __ CmpUnD(FTMP, a, b);
768 __ Bc1eqz(FTMP, &noNaNs);
769
770 // One of the inputs is a NaN
771 __ CmpEqD(ftmp, a, a);
772 // If a == a then b is the NaN, otherwise a is the NaN.
773 __ SelD(ftmp, a, b);
774
775 if (ftmp != out) {
776 __ MovD(out, ftmp);
777 }
778
779 __ B(&done);
780
781 __ Bind(&noNaNs);
782
783 if (is_min) {
784 __ MinD(out, a, b);
785 } else {
786 __ MaxD(out, a, b);
787 }
788 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100789 DCHECK_EQ(type, DataType::Type::kFloat32);
Chris Larsenb74353a2015-11-20 09:07:09 -0800790 __ CmpUnS(FTMP, a, b);
791 __ Bc1eqz(FTMP, &noNaNs);
792
793 // One of the inputs is a NaN
794 __ CmpEqS(ftmp, a, a);
795 // If a == a then b is the NaN, otherwise a is the NaN.
796 __ SelS(ftmp, a, b);
797
798 if (ftmp != out) {
799 __ MovS(out, ftmp);
800 }
801
802 __ B(&done);
803
804 __ Bind(&noNaNs);
805
806 if (is_min) {
807 __ MinS(out, a, b);
808 } else {
809 __ MaxS(out, a, b);
810 }
811 }
812
813 __ Bind(&done);
814 } else {
815 MipsLabel ordered;
816 MipsLabel compare;
817 MipsLabel select;
818 MipsLabel done;
819
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100820 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800821 __ CunD(a, b);
822 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100823 DCHECK_EQ(type, DataType::Type::kFloat32);
Chris Larsenb74353a2015-11-20 09:07:09 -0800824 __ CunS(a, b);
825 }
826 __ Bc1f(&ordered);
827
828 // a or b (or both) is a NaN. Return one, which is a NaN.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100829 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800830 __ CeqD(b, b);
831 } else {
832 __ CeqS(b, b);
833 }
834 __ B(&select);
835
836 __ Bind(&ordered);
837
838 // Neither is a NaN.
839 // a == b? (-0.0 compares equal with +0.0)
840 // If equal, handle zeroes, else compare further.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100841 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800842 __ CeqD(a, b);
843 } else {
844 __ CeqS(a, b);
845 }
846 __ Bc1f(&compare);
847
848 // a == b either bit for bit or one is -0.0 and the other is +0.0.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100849 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800850 __ MoveFromFpuHigh(TMP, a);
851 __ MoveFromFpuHigh(AT, b);
852 } else {
853 __ Mfc1(TMP, a);
854 __ Mfc1(AT, b);
855 }
856
857 if (is_min) {
858 // -0.0 prevails over +0.0.
859 __ Or(TMP, TMP, AT);
860 } else {
861 // +0.0 prevails over -0.0.
862 __ And(TMP, TMP, AT);
863 }
864
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100865 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800866 __ Mfc1(AT, a);
867 __ Mtc1(AT, out);
868 __ MoveToFpuHigh(TMP, out);
869 } else {
870 __ Mtc1(TMP, out);
871 }
872 __ B(&done);
873
874 __ Bind(&compare);
875
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100876 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800877 if (is_min) {
878 // return (a <= b) ? a : b;
879 __ ColeD(a, b);
880 } else {
881 // return (a >= b) ? a : b;
882 __ ColeD(b, a); // b <= a
883 }
884 } else {
885 if (is_min) {
886 // return (a <= b) ? a : b;
887 __ ColeS(a, b);
888 } else {
889 // return (a >= b) ? a : b;
890 __ ColeS(b, a); // b <= a
891 }
892 }
893
894 __ Bind(&select);
895
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100896 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800897 __ MovtD(out, a);
898 __ MovfD(out, b);
899 } else {
900 __ MovtS(out, a);
901 __ MovfS(out, b);
902 }
903
904 __ Bind(&done);
905 }
906}
907
Vladimir Markoca6fff82017-10-03 14:49:14 +0100908static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
909 LocationSummary* locations =
910 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsenb74353a2015-11-20 09:07:09 -0800911 locations->SetInAt(0, Location::RequiresFpuRegister());
912 locations->SetInAt(1, Location::RequiresFpuRegister());
913 locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap);
914}
915
916// double java.lang.Math.min(double, double)
917void IntrinsicLocationsBuilderMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100918 CreateFPFPToFPLocations(allocator_, invoke);
Chris Larsenb74353a2015-11-20 09:07:09 -0800919}
920
921void IntrinsicCodeGeneratorMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) {
922 GenMinMaxFP(invoke->GetLocations(),
923 /* is_min */ true,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100924 DataType::Type::kFloat64,
Chris Larsenb74353a2015-11-20 09:07:09 -0800925 IsR6(),
926 GetAssembler());
927}
928
929// float java.lang.Math.min(float, float)
930void IntrinsicLocationsBuilderMIPS::VisitMathMinFloatFloat(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100931 CreateFPFPToFPLocations(allocator_, invoke);
Chris Larsenb74353a2015-11-20 09:07:09 -0800932}
933
934void IntrinsicCodeGeneratorMIPS::VisitMathMinFloatFloat(HInvoke* invoke) {
935 GenMinMaxFP(invoke->GetLocations(),
936 /* is_min */ true,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100937 DataType::Type::kFloat32,
Chris Larsenb74353a2015-11-20 09:07:09 -0800938 IsR6(),
939 GetAssembler());
940}
941
942// double java.lang.Math.max(double, double)
943void IntrinsicLocationsBuilderMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100944 CreateFPFPToFPLocations(allocator_, invoke);
Chris Larsenb74353a2015-11-20 09:07:09 -0800945}
946
947void IntrinsicCodeGeneratorMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) {
948 GenMinMaxFP(invoke->GetLocations(),
949 /* is_min */ false,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100950 DataType::Type::kFloat64,
Chris Larsenb74353a2015-11-20 09:07:09 -0800951 IsR6(),
952 GetAssembler());
953}
954
955// float java.lang.Math.max(float, float)
956void IntrinsicLocationsBuilderMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100957 CreateFPFPToFPLocations(allocator_, invoke);
Chris Larsenb74353a2015-11-20 09:07:09 -0800958}
959
960void IntrinsicCodeGeneratorMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) {
961 GenMinMaxFP(invoke->GetLocations(),
962 /* is_min */ false,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100963 DataType::Type::kFloat32,
Chris Larsenb74353a2015-11-20 09:07:09 -0800964 IsR6(),
965 GetAssembler());
966}
967
Vladimir Markoca6fff82017-10-03 14:49:14 +0100968static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
969 LocationSummary* locations =
970 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsenb74353a2015-11-20 09:07:09 -0800971 locations->SetInAt(0, Location::RequiresRegister());
972 locations->SetInAt(1, Location::RequiresRegister());
973 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
974}
975
976static void GenMinMax(LocationSummary* locations,
977 bool is_min,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100978 DataType::Type type,
Chris Larsenb74353a2015-11-20 09:07:09 -0800979 bool is_R6,
980 MipsAssembler* assembler) {
981 if (is_R6) {
982 // Some architectures, such as ARM and MIPS (prior to r6), have a
983 // conditional move instruction which only changes the target
984 // (output) register if the condition is true (MIPS prior to r6 had
985 // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions
986 // always change the target (output) register. If the condition is
987 // true the output register gets the contents of the "rs" register;
988 // otherwise, the output register is set to zero. One consequence
989 // of this is that to implement something like "rd = c==0 ? rs : rt"
990 // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions.
991 // After executing this pair of instructions one of the output
992 // registers from the pair will necessarily contain zero. Then the
993 // code ORs the output registers from the SELEQZ/SELNEZ instructions
994 // to get the final result.
995 //
996 // The initial test to see if the output register is same as the
997 // first input register is needed to make sure that value in the
998 // first input register isn't clobbered before we've finished
999 // computing the output value. The logic in the corresponding else
1000 // clause performs the same task but makes sure the second input
1001 // register isn't clobbered in the event that it's the same register
1002 // as the output register; the else clause also handles the case
1003 // where the output register is distinct from both the first, and the
1004 // second input registers.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001005 if (type == DataType::Type::kInt64) {
Chris Larsenb74353a2015-11-20 09:07:09 -08001006 Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
1007 Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
1008 Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
1009 Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
1010 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
1011 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
1012
1013 MipsLabel compare_done;
1014
1015 if (a_lo == b_lo) {
1016 if (out_lo != a_lo) {
1017 __ Move(out_lo, a_lo);
1018 __ Move(out_hi, a_hi);
1019 }
1020 } else {
1021 __ Slt(TMP, b_hi, a_hi);
1022 __ Bne(b_hi, a_hi, &compare_done);
1023
1024 __ Sltu(TMP, b_lo, a_lo);
1025
1026 __ Bind(&compare_done);
1027
1028 if (is_min) {
1029 __ Seleqz(AT, a_lo, TMP);
1030 __ Selnez(out_lo, b_lo, TMP); // Safe even if out_lo == a_lo/b_lo
1031 // because at this point we're
1032 // done using a_lo/b_lo.
1033 } else {
1034 __ Selnez(AT, a_lo, TMP);
1035 __ Seleqz(out_lo, b_lo, TMP); // ditto
1036 }
1037 __ Or(out_lo, out_lo, AT);
1038 if (is_min) {
1039 __ Seleqz(AT, a_hi, TMP);
1040 __ Selnez(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi
1041 } else {
1042 __ Selnez(AT, a_hi, TMP);
1043 __ Seleqz(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi
1044 }
1045 __ Or(out_hi, out_hi, AT);
1046 }
1047 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001048 DCHECK_EQ(type, DataType::Type::kInt32);
Chris Larsenb74353a2015-11-20 09:07:09 -08001049 Register a = locations->InAt(0).AsRegister<Register>();
1050 Register b = locations->InAt(1).AsRegister<Register>();
1051 Register out = locations->Out().AsRegister<Register>();
1052
1053 if (a == b) {
1054 if (out != a) {
1055 __ Move(out, a);
1056 }
1057 } else {
1058 __ Slt(AT, b, a);
1059 if (is_min) {
1060 __ Seleqz(TMP, a, AT);
1061 __ Selnez(AT, b, AT);
1062 } else {
1063 __ Selnez(TMP, a, AT);
1064 __ Seleqz(AT, b, AT);
1065 }
1066 __ Or(out, TMP, AT);
1067 }
1068 }
1069 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001070 if (type == DataType::Type::kInt64) {
Chris Larsenb74353a2015-11-20 09:07:09 -08001071 Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
1072 Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
1073 Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
1074 Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
1075 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
1076 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
1077
1078 MipsLabel compare_done;
1079
1080 if (a_lo == b_lo) {
1081 if (out_lo != a_lo) {
1082 __ Move(out_lo, a_lo);
1083 __ Move(out_hi, a_hi);
1084 }
1085 } else {
1086 __ Slt(TMP, a_hi, b_hi);
1087 __ Bne(a_hi, b_hi, &compare_done);
1088
1089 __ Sltu(TMP, a_lo, b_lo);
1090
1091 __ Bind(&compare_done);
1092
1093 if (is_min) {
1094 if (out_lo != a_lo) {
1095 __ Movn(out_hi, a_hi, TMP);
1096 __ Movn(out_lo, a_lo, TMP);
1097 }
1098 if (out_lo != b_lo) {
1099 __ Movz(out_hi, b_hi, TMP);
1100 __ Movz(out_lo, b_lo, TMP);
1101 }
1102 } else {
1103 if (out_lo != a_lo) {
1104 __ Movz(out_hi, a_hi, TMP);
1105 __ Movz(out_lo, a_lo, TMP);
1106 }
1107 if (out_lo != b_lo) {
1108 __ Movn(out_hi, b_hi, TMP);
1109 __ Movn(out_lo, b_lo, TMP);
1110 }
1111 }
1112 }
1113 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001114 DCHECK_EQ(type, DataType::Type::kInt32);
Chris Larsenb74353a2015-11-20 09:07:09 -08001115 Register a = locations->InAt(0).AsRegister<Register>();
1116 Register b = locations->InAt(1).AsRegister<Register>();
1117 Register out = locations->Out().AsRegister<Register>();
1118
1119 if (a == b) {
1120 if (out != a) {
1121 __ Move(out, a);
1122 }
1123 } else {
1124 __ Slt(AT, a, b);
1125 if (is_min) {
1126 if (out != a) {
1127 __ Movn(out, a, AT);
1128 }
1129 if (out != b) {
1130 __ Movz(out, b, AT);
1131 }
1132 } else {
1133 if (out != a) {
1134 __ Movz(out, a, AT);
1135 }
1136 if (out != b) {
1137 __ Movn(out, b, AT);
1138 }
1139 }
1140 }
1141 }
1142 }
1143}
1144
1145// int java.lang.Math.min(int, int)
1146void IntrinsicLocationsBuilderMIPS::VisitMathMinIntInt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001147 CreateIntIntToIntLocations(allocator_, invoke);
Chris Larsenb74353a2015-11-20 09:07:09 -08001148}
1149
1150void IntrinsicCodeGeneratorMIPS::VisitMathMinIntInt(HInvoke* invoke) {
1151 GenMinMax(invoke->GetLocations(),
1152 /* is_min */ true,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001153 DataType::Type::kInt32,
Chris Larsenb74353a2015-11-20 09:07:09 -08001154 IsR6(),
1155 GetAssembler());
1156}
1157
1158// long java.lang.Math.min(long, long)
1159void IntrinsicLocationsBuilderMIPS::VisitMathMinLongLong(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001160 CreateIntIntToIntLocations(allocator_, invoke);
Chris Larsenb74353a2015-11-20 09:07:09 -08001161}
1162
1163void IntrinsicCodeGeneratorMIPS::VisitMathMinLongLong(HInvoke* invoke) {
1164 GenMinMax(invoke->GetLocations(),
1165 /* is_min */ true,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001166 DataType::Type::kInt64,
Chris Larsenb74353a2015-11-20 09:07:09 -08001167 IsR6(),
1168 GetAssembler());
1169}
1170
1171// int java.lang.Math.max(int, int)
1172void IntrinsicLocationsBuilderMIPS::VisitMathMaxIntInt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001173 CreateIntIntToIntLocations(allocator_, invoke);
Chris Larsenb74353a2015-11-20 09:07:09 -08001174}
1175
1176void IntrinsicCodeGeneratorMIPS::VisitMathMaxIntInt(HInvoke* invoke) {
1177 GenMinMax(invoke->GetLocations(),
1178 /* is_min */ false,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001179 DataType::Type::kInt32,
Chris Larsenb74353a2015-11-20 09:07:09 -08001180 IsR6(),
1181 GetAssembler());
1182}
1183
1184// long java.lang.Math.max(long, long)
1185void IntrinsicLocationsBuilderMIPS::VisitMathMaxLongLong(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001186 CreateIntIntToIntLocations(allocator_, invoke);
Chris Larsenb74353a2015-11-20 09:07:09 -08001187}
1188
1189void IntrinsicCodeGeneratorMIPS::VisitMathMaxLongLong(HInvoke* invoke) {
1190 GenMinMax(invoke->GetLocations(),
1191 /* is_min */ false,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001192 DataType::Type::kInt64,
Chris Larsenb74353a2015-11-20 09:07:09 -08001193 IsR6(),
1194 GetAssembler());
1195}
1196
1197// double java.lang.Math.sqrt(double)
1198void IntrinsicLocationsBuilderMIPS::VisitMathSqrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001199 CreateFPToFPLocations(allocator_, invoke);
Chris Larsenb74353a2015-11-20 09:07:09 -08001200}
1201
1202void IntrinsicCodeGeneratorMIPS::VisitMathSqrt(HInvoke* invoke) {
1203 LocationSummary* locations = invoke->GetLocations();
1204 MipsAssembler* assembler = GetAssembler();
1205 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
1206 FRegister out = locations->Out().AsFpuRegister<FRegister>();
1207
1208 __ SqrtD(out, in);
1209}
1210
Chris Larsen3acee732015-11-18 13:31:08 -08001211// byte libcore.io.Memory.peekByte(long address)
1212void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001213 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen3acee732015-11-18 13:31:08 -08001214}
1215
1216void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekByte(HInvoke* invoke) {
1217 MipsAssembler* assembler = GetAssembler();
1218 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1219 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
1220
1221 __ Lb(out, adr, 0);
1222}
1223
1224// short libcore.io.Memory.peekShort(long address)
1225void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001226 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen3acee732015-11-18 13:31:08 -08001227}
1228
1229void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekShortNative(HInvoke* invoke) {
1230 MipsAssembler* assembler = GetAssembler();
1231 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1232 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
1233
1234 if (IsR6()) {
1235 __ Lh(out, adr, 0);
1236 } else if (IsR2OrNewer()) {
1237 // Unlike for words, there are no lhl/lhr instructions to load
1238 // unaligned halfwords so the code loads individual bytes, in case
1239 // the address isn't halfword-aligned, and assembles them into a
1240 // signed halfword.
1241 __ Lb(AT, adr, 1); // This byte must be sign-extended.
1242 __ Lb(out, adr, 0); // This byte can be either sign-extended, or
1243 // zero-extended because the following
1244 // instruction overwrites the sign bits.
1245 __ Ins(out, AT, 8, 24);
1246 } else {
1247 __ Lbu(AT, adr, 0); // This byte must be zero-extended. If it's not
1248 // the "or" instruction below will destroy the upper
1249 // 24 bits of the final result.
1250 __ Lb(out, adr, 1); // This byte must be sign-extended.
1251 __ Sll(out, out, 8);
1252 __ Or(out, out, AT);
1253 }
1254}
1255
1256// int libcore.io.Memory.peekInt(long address)
1257void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001258 CreateIntToIntLocations(allocator_, invoke, Location::kOutputOverlap);
Chris Larsen3acee732015-11-18 13:31:08 -08001259}
1260
1261void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekIntNative(HInvoke* invoke) {
1262 MipsAssembler* assembler = GetAssembler();
1263 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1264 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
1265
1266 if (IsR6()) {
1267 __ Lw(out, adr, 0);
1268 } else {
1269 __ Lwr(out, adr, 0);
1270 __ Lwl(out, adr, 3);
1271 }
1272}
1273
1274// long libcore.io.Memory.peekLong(long address)
1275void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001276 CreateIntToIntLocations(allocator_, invoke, Location::kOutputOverlap);
Chris Larsen3acee732015-11-18 13:31:08 -08001277}
1278
1279void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekLongNative(HInvoke* invoke) {
1280 MipsAssembler* assembler = GetAssembler();
1281 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1282 Register out_lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
1283 Register out_hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
1284
1285 if (IsR6()) {
1286 __ Lw(out_lo, adr, 0);
1287 __ Lw(out_hi, adr, 4);
1288 } else {
1289 __ Lwr(out_lo, adr, 0);
1290 __ Lwl(out_lo, adr, 3);
1291 __ Lwr(out_hi, adr, 4);
1292 __ Lwl(out_hi, adr, 7);
1293 }
1294}
1295
Vladimir Markoca6fff82017-10-03 14:49:14 +01001296static void CreateIntIntToVoidLocations(ArenaAllocator* allocator, HInvoke* invoke) {
1297 LocationSummary* locations =
1298 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsen3acee732015-11-18 13:31:08 -08001299 locations->SetInAt(0, Location::RequiresRegister());
1300 locations->SetInAt(1, Location::RequiresRegister());
1301}
1302
1303// void libcore.io.Memory.pokeByte(long address, byte value)
1304void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001305 CreateIntIntToVoidLocations(allocator_, invoke);
Chris Larsen3acee732015-11-18 13:31:08 -08001306}
1307
1308void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeByte(HInvoke* invoke) {
1309 MipsAssembler* assembler = GetAssembler();
1310 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1311 Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>();
1312
1313 __ Sb(val, adr, 0);
1314}
1315
1316// void libcore.io.Memory.pokeShort(long address, short value)
1317void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001318 CreateIntIntToVoidLocations(allocator_, invoke);
Chris Larsen3acee732015-11-18 13:31:08 -08001319}
1320
1321void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeShortNative(HInvoke* invoke) {
1322 MipsAssembler* assembler = GetAssembler();
1323 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1324 Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>();
1325
1326 if (IsR6()) {
1327 __ Sh(val, adr, 0);
1328 } else {
1329 // Unlike for words, there are no shl/shr instructions to store
1330 // unaligned halfwords so the code stores individual bytes, in case
1331 // the address isn't halfword-aligned.
1332 __ Sb(val, adr, 0);
1333 __ Srl(AT, val, 8);
1334 __ Sb(AT, adr, 1);
1335 }
1336}
1337
1338// void libcore.io.Memory.pokeInt(long address, int value)
1339void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001340 CreateIntIntToVoidLocations(allocator_, invoke);
Chris Larsen3acee732015-11-18 13:31:08 -08001341}
1342
1343void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeIntNative(HInvoke* invoke) {
1344 MipsAssembler* assembler = GetAssembler();
1345 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1346 Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>();
1347
1348 if (IsR6()) {
1349 __ Sw(val, adr, 0);
1350 } else {
1351 __ Swr(val, adr, 0);
1352 __ Swl(val, adr, 3);
1353 }
1354}
1355
1356// void libcore.io.Memory.pokeLong(long address, long value)
1357void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001358 CreateIntIntToVoidLocations(allocator_, invoke);
Chris Larsen3acee732015-11-18 13:31:08 -08001359}
1360
1361void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) {
1362 MipsAssembler* assembler = GetAssembler();
1363 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1364 Register val_lo = invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>();
1365 Register val_hi = invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>();
1366
1367 if (IsR6()) {
1368 __ Sw(val_lo, adr, 0);
1369 __ Sw(val_hi, adr, 4);
1370 } else {
1371 __ Swr(val_lo, adr, 0);
1372 __ Swl(val_lo, adr, 3);
1373 __ Swr(val_hi, adr, 4);
1374 __ Swl(val_hi, adr, 7);
1375 }
1376}
1377
Chris Larsencf283da2016-01-19 16:45:35 -08001378// Thread java.lang.Thread.currentThread()
1379void IntrinsicLocationsBuilderMIPS::VisitThreadCurrentThread(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001380 LocationSummary* locations =
1381 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsencf283da2016-01-19 16:45:35 -08001382 locations->SetOut(Location::RequiresRegister());
1383}
1384
1385void IntrinsicCodeGeneratorMIPS::VisitThreadCurrentThread(HInvoke* invoke) {
1386 MipsAssembler* assembler = GetAssembler();
1387 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
1388
1389 __ LoadFromOffset(kLoadWord,
1390 out,
1391 TR,
1392 Thread::PeerOffset<kMipsPointerSize>().Int32Value());
1393}
1394
Vladimir Markoca6fff82017-10-03 14:49:14 +01001395static void CreateIntIntIntToIntLocations(ArenaAllocator* allocator,
Alexey Frunze15958152017-02-09 19:08:30 -08001396 HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001397 DataType::Type type) {
Alexey Frunze15958152017-02-09 19:08:30 -08001398 bool can_call = kEmitCompilerReadBarrier &&
1399 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
1400 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Vladimir Markoca6fff82017-10-03 14:49:14 +01001401 LocationSummary* locations =
1402 new (allocator) LocationSummary(invoke,
1403 can_call
1404 ? LocationSummary::kCallOnSlowPath
1405 : LocationSummary::kNoCall,
1406 kIntrinsified);
Alexey Frunzec61c0762017-04-10 13:54:23 -07001407 if (can_call && kUseBakerReadBarrier) {
1408 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
1409 }
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001410 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1411 locations->SetInAt(1, Location::RequiresRegister());
1412 locations->SetInAt(2, Location::RequiresRegister());
Alexey Frunze15958152017-02-09 19:08:30 -08001413 locations->SetOut(Location::RequiresRegister(),
1414 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001415 if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Alexey Frunze15958152017-02-09 19:08:30 -08001416 // We need a temporary register for the read barrier marking slow
1417 // path in InstructionCodeGeneratorMIPS::GenerateReferenceLoadWithBakerReadBarrier.
1418 locations->AddTemp(Location::RequiresRegister());
1419 }
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001420}
1421
Alexey Frunze15958152017-02-09 19:08:30 -08001422// Note that the caller must supply a properly aligned memory address.
1423// If they do not, the behavior is undefined (atomicity not guaranteed, exception may occur).
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001424static void GenUnsafeGet(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001425 DataType::Type type,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001426 bool is_volatile,
1427 bool is_R6,
1428 CodeGeneratorMIPS* codegen) {
1429 LocationSummary* locations = invoke->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001430 DCHECK((type == DataType::Type::kInt32) ||
1431 (type == DataType::Type::kInt64) ||
1432 (type == DataType::Type::kReference)) << type;
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001433 MipsAssembler* assembler = codegen->GetAssembler();
Alexey Frunze15958152017-02-09 19:08:30 -08001434 // Target register.
1435 Location trg_loc = locations->Out();
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001436 // Object pointer.
Alexey Frunze15958152017-02-09 19:08:30 -08001437 Location base_loc = locations->InAt(1);
1438 Register base = base_loc.AsRegister<Register>();
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001439 // The "offset" argument is passed as a "long". Since this code is for
1440 // a 32-bit processor, we can only use 32-bit addresses, so we only
1441 // need the low 32-bits of offset.
Alexey Frunze15958152017-02-09 19:08:30 -08001442 Location offset_loc = locations->InAt(2);
1443 Register offset_lo = offset_loc.AsRegisterPairLow<Register>();
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001444
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001445 if (!(kEmitCompilerReadBarrier && kUseBakerReadBarrier && (type == DataType::Type::kReference))) {
Alexey Frunze15958152017-02-09 19:08:30 -08001446 __ Addu(TMP, base, offset_lo);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001447 }
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001448
Alexey Frunze15958152017-02-09 19:08:30 -08001449 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001450 case DataType::Type::kInt64: {
Alexey Frunze15958152017-02-09 19:08:30 -08001451 Register trg_lo = trg_loc.AsRegisterPairLow<Register>();
1452 Register trg_hi = trg_loc.AsRegisterPairHigh<Register>();
1453 CHECK(!is_volatile); // TODO: support atomic 8-byte volatile loads.
1454 if (is_R6) {
1455 __ Lw(trg_lo, TMP, 0);
1456 __ Lw(trg_hi, TMP, 4);
1457 } else {
1458 __ Lwr(trg_lo, TMP, 0);
1459 __ Lwl(trg_lo, TMP, 3);
1460 __ Lwr(trg_hi, TMP, 4);
1461 __ Lwl(trg_hi, TMP, 7);
1462 }
1463 break;
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001464 }
Alexey Frunzec061de12017-02-14 13:27:23 -08001465
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001466 case DataType::Type::kInt32: {
Alexey Frunze15958152017-02-09 19:08:30 -08001467 Register trg = trg_loc.AsRegister<Register>();
1468 if (is_R6) {
1469 __ Lw(trg, TMP, 0);
1470 } else {
1471 __ Lwr(trg, TMP, 0);
1472 __ Lwl(trg, TMP, 3);
1473 }
1474 if (is_volatile) {
1475 __ Sync(0);
1476 }
1477 break;
Alexey Frunzec061de12017-02-14 13:27:23 -08001478 }
Alexey Frunze15958152017-02-09 19:08:30 -08001479
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001480 case DataType::Type::kReference: {
Alexey Frunze15958152017-02-09 19:08:30 -08001481 Register trg = trg_loc.AsRegister<Register>();
1482 if (kEmitCompilerReadBarrier) {
1483 if (kUseBakerReadBarrier) {
1484 Location temp = locations->GetTemp(0);
1485 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
1486 trg_loc,
1487 base,
1488 /* offset */ 0U,
1489 /* index */ offset_loc,
1490 TIMES_1,
1491 temp,
1492 /* needs_null_check */ false);
1493 if (is_volatile) {
1494 __ Sync(0);
1495 }
1496 } else {
1497 if (is_R6) {
1498 __ Lw(trg, TMP, 0);
1499 } else {
1500 __ Lwr(trg, TMP, 0);
1501 __ Lwl(trg, TMP, 3);
1502 }
1503 if (is_volatile) {
1504 __ Sync(0);
1505 }
1506 codegen->GenerateReadBarrierSlow(invoke,
1507 trg_loc,
1508 trg_loc,
1509 base_loc,
1510 /* offset */ 0U,
1511 /* index */ offset_loc);
1512 }
1513 } else {
1514 if (is_R6) {
1515 __ Lw(trg, TMP, 0);
1516 } else {
1517 __ Lwr(trg, TMP, 0);
1518 __ Lwl(trg, TMP, 3);
1519 }
1520 if (is_volatile) {
1521 __ Sync(0);
1522 }
1523 __ MaybeUnpoisonHeapReference(trg);
1524 }
1525 break;
1526 }
1527
1528 default:
1529 LOG(FATAL) << "Unexpected type " << type;
1530 UNREACHABLE();
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001531 }
1532}
1533
1534// int sun.misc.Unsafe.getInt(Object o, long offset)
1535void IntrinsicLocationsBuilderMIPS::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001536 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001537}
1538
1539void IntrinsicCodeGeneratorMIPS::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001540 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ false, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001541}
1542
1543// int sun.misc.Unsafe.getIntVolatile(Object o, long offset)
1544void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001545 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001546}
1547
1548void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001549 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ true, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001550}
1551
1552// long sun.misc.Unsafe.getLong(Object o, long offset)
1553void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001554 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001555}
1556
1557void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001558 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ false, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001559}
1560
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001561// Object sun.misc.Unsafe.getObject(Object o, long offset)
1562void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001563 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001564}
1565
1566void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001567 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ false, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001568}
1569
1570// Object sun.misc.Unsafe.getObjectVolatile(Object o, long offset)
1571void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001572 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kReference);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001573}
1574
1575void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001576 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ true, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001577}
1578
Vladimir Markoca6fff82017-10-03 14:49:14 +01001579static void CreateIntIntIntIntToVoidLocations(ArenaAllocator* allocator, HInvoke* invoke) {
1580 LocationSummary* locations =
1581 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001582 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1583 locations->SetInAt(1, Location::RequiresRegister());
1584 locations->SetInAt(2, Location::RequiresRegister());
1585 locations->SetInAt(3, Location::RequiresRegister());
1586}
1587
Alexey Frunze15958152017-02-09 19:08:30 -08001588// Note that the caller must supply a properly aligned memory address.
1589// If they do not, the behavior is undefined (atomicity not guaranteed, exception may occur).
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001590static void GenUnsafePut(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001591 DataType::Type type,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001592 bool is_volatile,
1593 bool is_ordered,
1594 bool is_R6,
1595 CodeGeneratorMIPS* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001596 DCHECK((type == DataType::Type::kInt32) ||
1597 (type == DataType::Type::kInt64) ||
1598 (type == DataType::Type::kReference)) << type;
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001599 MipsAssembler* assembler = codegen->GetAssembler();
1600 // Object pointer.
1601 Register base = locations->InAt(1).AsRegister<Register>();
1602 // The "offset" argument is passed as a "long", i.e., it's 64-bits in
1603 // size. Since this code is for a 32-bit processor, we can only use
1604 // 32-bit addresses, so we only need the low 32-bits of offset.
1605 Register offset_lo = locations->InAt(2).AsRegisterPairLow<Register>();
1606
1607 __ Addu(TMP, base, offset_lo);
1608 if (is_volatile || is_ordered) {
1609 __ Sync(0);
1610 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001611 if ((type == DataType::Type::kInt32) || (type == DataType::Type::kReference)) {
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001612 Register value = locations->InAt(3).AsRegister<Register>();
1613
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001614 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Alexey Frunzec061de12017-02-14 13:27:23 -08001615 __ PoisonHeapReference(AT, value);
1616 value = AT;
1617 }
1618
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001619 if (is_R6) {
1620 __ Sw(value, TMP, 0);
1621 } else {
1622 __ Swr(value, TMP, 0);
1623 __ Swl(value, TMP, 3);
1624 }
1625 } else {
1626 Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
1627 Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
Alexey Frunze15958152017-02-09 19:08:30 -08001628 CHECK(!is_volatile); // TODO: support atomic 8-byte volatile stores.
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001629 if (is_R6) {
1630 __ Sw(value_lo, TMP, 0);
1631 __ Sw(value_hi, TMP, 4);
1632 } else {
1633 __ Swr(value_lo, TMP, 0);
1634 __ Swl(value_lo, TMP, 3);
1635 __ Swr(value_hi, TMP, 4);
1636 __ Swl(value_hi, TMP, 7);
1637 }
1638 }
1639
1640 if (is_volatile) {
1641 __ Sync(0);
1642 }
1643
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001644 if (type == DataType::Type::kReference) {
Goran Jakovljevice114da22016-12-26 14:21:43 +01001645 bool value_can_be_null = true; // TODO: Worth finding out this information?
1646 codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>(), value_can_be_null);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001647 }
1648}
1649
1650// void sun.misc.Unsafe.putInt(Object o, long offset, int x)
1651void IntrinsicLocationsBuilderMIPS::VisitUnsafePut(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001652 CreateIntIntIntIntToVoidLocations(allocator_, invoke);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001653}
1654
1655void IntrinsicCodeGeneratorMIPS::VisitUnsafePut(HInvoke* invoke) {
1656 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001657 DataType::Type::kInt32,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001658 /* is_volatile */ false,
1659 /* is_ordered */ false,
1660 IsR6(),
1661 codegen_);
1662}
1663
1664// void sun.misc.Unsafe.putOrderedInt(Object o, long offset, int x)
1665void IntrinsicLocationsBuilderMIPS::VisitUnsafePutOrdered(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001666 CreateIntIntIntIntToVoidLocations(allocator_, invoke);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001667}
1668
1669void IntrinsicCodeGeneratorMIPS::VisitUnsafePutOrdered(HInvoke* invoke) {
1670 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001671 DataType::Type::kInt32,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001672 /* is_volatile */ false,
1673 /* is_ordered */ true,
1674 IsR6(),
1675 codegen_);
1676}
1677
1678// void sun.misc.Unsafe.putIntVolatile(Object o, long offset, int x)
1679void IntrinsicLocationsBuilderMIPS::VisitUnsafePutVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001680 CreateIntIntIntIntToVoidLocations(allocator_, invoke);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001681}
1682
1683void IntrinsicCodeGeneratorMIPS::VisitUnsafePutVolatile(HInvoke* invoke) {
1684 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001685 DataType::Type::kInt32,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001686 /* is_volatile */ true,
1687 /* is_ordered */ false,
1688 IsR6(),
1689 codegen_);
1690}
1691
1692// void sun.misc.Unsafe.putObject(Object o, long offset, Object x)
1693void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObject(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001694 CreateIntIntIntIntToVoidLocations(allocator_, invoke);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001695}
1696
1697void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObject(HInvoke* invoke) {
1698 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001699 DataType::Type::kReference,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001700 /* is_volatile */ false,
1701 /* is_ordered */ false,
1702 IsR6(),
1703 codegen_);
1704}
1705
1706// void sun.misc.Unsafe.putOrderedObject(Object o, long offset, Object x)
1707void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001708 CreateIntIntIntIntToVoidLocations(allocator_, invoke);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001709}
1710
1711void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1712 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001713 DataType::Type::kReference,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001714 /* is_volatile */ false,
1715 /* is_ordered */ true,
1716 IsR6(),
1717 codegen_);
1718}
1719
1720// void sun.misc.Unsafe.putObjectVolatile(Object o, long offset, Object x)
1721void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001722 CreateIntIntIntIntToVoidLocations(allocator_, invoke);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001723}
1724
1725void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1726 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001727 DataType::Type::kReference,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001728 /* is_volatile */ true,
1729 /* is_ordered */ false,
1730 IsR6(),
1731 codegen_);
1732}
1733
1734// void sun.misc.Unsafe.putLong(Object o, long offset, long x)
1735void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLong(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001736 CreateIntIntIntIntToVoidLocations(allocator_, invoke);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001737}
1738
1739void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLong(HInvoke* invoke) {
1740 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001741 DataType::Type::kInt64,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001742 /* is_volatile */ false,
1743 /* is_ordered */ false,
1744 IsR6(),
1745 codegen_);
1746}
1747
1748// void sun.misc.Unsafe.putOrderedLong(Object o, long offset, long x)
1749void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001750 CreateIntIntIntIntToVoidLocations(allocator_, invoke);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001751}
1752
1753void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1754 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001755 DataType::Type::kInt64,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001756 /* is_volatile */ false,
1757 /* is_ordered */ true,
1758 IsR6(),
1759 codegen_);
1760}
1761
Vladimir Markoca6fff82017-10-03 14:49:14 +01001762static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* allocator, HInvoke* invoke) {
Alexey Frunze15958152017-02-09 19:08:30 -08001763 bool can_call = kEmitCompilerReadBarrier &&
1764 kUseBakerReadBarrier &&
1765 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Vladimir Markoca6fff82017-10-03 14:49:14 +01001766 LocationSummary* locations =
1767 new (allocator) LocationSummary(invoke,
1768 can_call
1769 ? LocationSummary::kCallOnSlowPath
1770 : LocationSummary::kNoCall,
1771 kIntrinsified);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001772 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1773 locations->SetInAt(1, Location::RequiresRegister());
1774 locations->SetInAt(2, Location::RequiresRegister());
1775 locations->SetInAt(3, Location::RequiresRegister());
1776 locations->SetInAt(4, Location::RequiresRegister());
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001777 locations->SetOut(Location::RequiresRegister());
Alexey Frunze15958152017-02-09 19:08:30 -08001778
1779 // Temporary register used in CAS by (Baker) read barrier.
1780 if (can_call) {
1781 locations->AddTemp(Location::RequiresRegister());
1782 }
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001783}
1784
Alexey Frunze15958152017-02-09 19:08:30 -08001785// Note that the caller must supply a properly aligned memory address.
1786// If they do not, the behavior is undefined (atomicity not guaranteed, exception may occur).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001787static void GenCas(HInvoke* invoke, DataType::Type type, CodeGeneratorMIPS* codegen) {
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001788 MipsAssembler* assembler = codegen->GetAssembler();
Alexey Frunze15958152017-02-09 19:08:30 -08001789 LocationSummary* locations = invoke->GetLocations();
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001790 bool isR6 = codegen->GetInstructionSetFeatures().IsR6();
1791 Register base = locations->InAt(1).AsRegister<Register>();
Alexey Frunze15958152017-02-09 19:08:30 -08001792 Location offset_loc = locations->InAt(2);
1793 Register offset_lo = offset_loc.AsRegisterPairLow<Register>();
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001794 Register expected = locations->InAt(3).AsRegister<Register>();
1795 Register value = locations->InAt(4).AsRegister<Register>();
Alexey Frunze15958152017-02-09 19:08:30 -08001796 Location out_loc = locations->Out();
1797 Register out = out_loc.AsRegister<Register>();
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001798
1799 DCHECK_NE(base, out);
1800 DCHECK_NE(offset_lo, out);
1801 DCHECK_NE(expected, out);
1802
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001803 if (type == DataType::Type::kReference) {
Alexey Frunze15958152017-02-09 19:08:30 -08001804 // The only read barrier implementation supporting the
1805 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1806 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1807
1808 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
1809 // object and scan the receiver at the next GC for nothing.
Goran Jakovljevice114da22016-12-26 14:21:43 +01001810 bool value_can_be_null = true; // TODO: Worth finding out this information?
1811 codegen->MarkGCCard(base, value, value_can_be_null);
Alexey Frunze15958152017-02-09 19:08:30 -08001812
1813 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1814 Location temp = locations->GetTemp(0);
1815 // Need to make sure the reference stored in the field is a to-space
1816 // one before attempting the CAS or the CAS could fail incorrectly.
1817 codegen->GenerateReferenceLoadWithBakerReadBarrier(
1818 invoke,
1819 out_loc, // Unused, used only as a "temporary" within the read barrier.
1820 base,
1821 /* offset */ 0u,
1822 /* index */ offset_loc,
1823 ScaleFactor::TIMES_1,
1824 temp,
1825 /* needs_null_check */ false,
1826 /* always_update_field */ true);
1827 }
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001828 }
1829
Alexey Frunzec061de12017-02-14 13:27:23 -08001830 MipsLabel loop_head, exit_loop;
1831 __ Addu(TMP, base, offset_lo);
1832
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001833 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Alexey Frunzec061de12017-02-14 13:27:23 -08001834 __ PoisonHeapReference(expected);
1835 // Do not poison `value`, if it is the same register as
1836 // `expected`, which has just been poisoned.
1837 if (value != expected) {
1838 __ PoisonHeapReference(value);
1839 }
1840 }
1841
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001842 // do {
1843 // tmp_value = [tmp_ptr] - expected;
1844 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1845 // result = tmp_value != 0;
1846
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001847 __ Sync(0);
1848 __ Bind(&loop_head);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001849 if ((type == DataType::Type::kInt32) || (type == DataType::Type::kReference)) {
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001850 if (isR6) {
1851 __ LlR6(out, TMP);
1852 } else {
1853 __ LlR2(out, TMP);
1854 }
1855 } else {
Alexey Frunzec061de12017-02-14 13:27:23 -08001856 LOG(FATAL) << "Unsupported op size " << type;
1857 UNREACHABLE();
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001858 }
1859 __ Subu(out, out, expected); // If we didn't get the 'expected'
1860 __ Sltiu(out, out, 1); // value, set 'out' to false, and
1861 __ Beqz(out, &exit_loop); // return.
1862 __ Move(out, value); // Use 'out' for the 'store conditional' instruction.
1863 // If we use 'value' directly, we would lose 'value'
1864 // in the case that the store fails. Whether the
1865 // store succeeds, or fails, it will load the
Roland Levillain5e8d5f02016-10-18 18:03:43 +01001866 // correct Boolean value into the 'out' register.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001867 // This test isn't really necessary. We only support DataType::Type::kInt,
1868 // DataType::Type::kReference, and we already verified that we're working on one
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001869 // of those two types. It's left here in case the code needs to support
1870 // other types in the future.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001871 if ((type == DataType::Type::kInt32) || (type == DataType::Type::kReference)) {
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001872 if (isR6) {
1873 __ ScR6(out, TMP);
1874 } else {
1875 __ ScR2(out, TMP);
1876 }
1877 }
1878 __ Beqz(out, &loop_head); // If we couldn't do the read-modify-write
1879 // cycle atomically then retry.
1880 __ Bind(&exit_loop);
1881 __ Sync(0);
Alexey Frunzec061de12017-02-14 13:27:23 -08001882
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001883 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Alexey Frunzec061de12017-02-14 13:27:23 -08001884 __ UnpoisonHeapReference(expected);
1885 // Do not unpoison `value`, if it is the same register as
1886 // `expected`, which has just been unpoisoned.
1887 if (value != expected) {
1888 __ UnpoisonHeapReference(value);
1889 }
1890 }
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001891}
1892
1893// boolean sun.misc.Unsafe.compareAndSwapInt(Object o, long offset, int expected, int x)
1894void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001895 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001896}
1897
1898void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001899 GenCas(invoke, DataType::Type::kInt32, codegen_);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001900}
1901
1902// boolean sun.misc.Unsafe.compareAndSwapObject(Object o, long offset, Object expected, Object x)
1903void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASObject(HInvoke* invoke) {
Alexey Frunze15958152017-02-09 19:08:30 -08001904 // The only read barrier implementation supporting the
1905 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1906 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
1907 return;
1908 }
1909
Vladimir Markoca6fff82017-10-03 14:49:14 +01001910 CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001911}
1912
1913void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASObject(HInvoke* invoke) {
Alexey Frunze15958152017-02-09 19:08:30 -08001914 // The only read barrier implementation supporting the
1915 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1916 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1917
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001918 GenCas(invoke, DataType::Type::kReference, codegen_);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001919}
1920
Chris Larsencf283da2016-01-19 16:45:35 -08001921// int java.lang.String.compareTo(String anotherString)
1922void IntrinsicLocationsBuilderMIPS::VisitStringCompareTo(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001923 LocationSummary* locations = new (allocator_) LocationSummary(
1924 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Chris Larsencf283da2016-01-19 16:45:35 -08001925 InvokeRuntimeCallingConvention calling_convention;
1926 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1927 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001928 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08001929 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
1930}
1931
1932void IntrinsicCodeGeneratorMIPS::VisitStringCompareTo(HInvoke* invoke) {
1933 MipsAssembler* assembler = GetAssembler();
1934 LocationSummary* locations = invoke->GetLocations();
1935
1936 // Note that the null check must have been done earlier.
1937 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1938
1939 Register argument = locations->InAt(1).AsRegister<Register>();
Vladimir Marko174b2e22017-10-12 13:34:49 +01001940 SlowPathCodeMIPS* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathMIPS(invoke);
Chris Larsencf283da2016-01-19 16:45:35 -08001941 codegen_->AddSlowPath(slow_path);
1942 __ Beqz(argument, slow_path->GetEntryLabel());
Serban Constantinescufca16662016-07-14 09:21:59 +01001943 codegen_->InvokeRuntime(kQuickStringCompareTo, invoke, invoke->GetDexPc(), slow_path);
Chris Larsencf283da2016-01-19 16:45:35 -08001944 __ Bind(slow_path->GetExitLabel());
1945}
1946
Chris Larsen16ba2b42015-11-02 10:58:31 -08001947// boolean java.lang.String.equals(Object anObject)
1948void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) {
Vladimir Markoda283052017-11-07 21:17:24 +00001949 if (kEmitCompilerReadBarrier &&
1950 !StringEqualsOptimizations(invoke).GetArgumentIsString() &&
1951 !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) {
1952 // No support for this odd case (String class is moveable, not in the boot image).
1953 return;
1954 }
1955
Vladimir Markoca6fff82017-10-03 14:49:14 +01001956 LocationSummary* locations =
1957 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsen16ba2b42015-11-02 10:58:31 -08001958 locations->SetInAt(0, Location::RequiresRegister());
1959 locations->SetInAt(1, Location::RequiresRegister());
1960 locations->SetOut(Location::RequiresRegister());
1961
1962 // Temporary registers to store lengths of strings and for calculations.
1963 locations->AddTemp(Location::RequiresRegister());
1964 locations->AddTemp(Location::RequiresRegister());
1965 locations->AddTemp(Location::RequiresRegister());
1966}
1967
1968void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) {
1969 MipsAssembler* assembler = GetAssembler();
1970 LocationSummary* locations = invoke->GetLocations();
1971
1972 Register str = locations->InAt(0).AsRegister<Register>();
1973 Register arg = locations->InAt(1).AsRegister<Register>();
1974 Register out = locations->Out().AsRegister<Register>();
1975
1976 Register temp1 = locations->GetTemp(0).AsRegister<Register>();
1977 Register temp2 = locations->GetTemp(1).AsRegister<Register>();
1978 Register temp3 = locations->GetTemp(2).AsRegister<Register>();
1979
1980 MipsLabel loop;
1981 MipsLabel end;
1982 MipsLabel return_true;
1983 MipsLabel return_false;
1984
1985 // Get offsets of count, value, and class fields within a string object.
1986 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1987 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1988 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1989
1990 // Note that the null check must have been done earlier.
1991 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1992
1993 // If the register containing the pointer to "this", and the register
1994 // containing the pointer to "anObject" are the same register then
1995 // "this", and "anObject" are the same object and we can
1996 // short-circuit the logic to a true result.
1997 if (str == arg) {
1998 __ LoadConst32(out, 1);
1999 return;
2000 }
Goran Jakovljevic64fa84f2017-02-27 13:14:57 +01002001 StringEqualsOptimizations optimizations(invoke);
2002 if (!optimizations.GetArgumentNotNull()) {
2003 // Check if input is null, return false if it is.
2004 __ Beqz(arg, &return_false);
2005 }
Chris Larsen16ba2b42015-11-02 10:58:31 -08002006
2007 // Reference equality check, return true if same reference.
2008 __ Beq(str, arg, &return_true);
2009
Goran Jakovljevic64fa84f2017-02-27 13:14:57 +01002010 if (!optimizations.GetArgumentIsString()) {
2011 // Instanceof check for the argument by comparing class fields.
2012 // All string objects must have the same type since String cannot be subclassed.
2013 // Receiver must be a string object, so its class field is equal to all strings' class fields.
2014 // If the argument is a string object, its class field must be equal to receiver's class field.
2015 __ Lw(temp1, str, class_offset);
2016 __ Lw(temp2, arg, class_offset);
2017 __ Bne(temp1, temp2, &return_false);
2018 }
Chris Larsen16ba2b42015-11-02 10:58:31 -08002019
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002020 // Load `count` fields of this and argument strings.
Chris Larsen16ba2b42015-11-02 10:58:31 -08002021 __ Lw(temp1, str, count_offset);
2022 __ Lw(temp2, arg, count_offset);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002023 // Check if `count` fields are equal, return false if they're not.
2024 // Also compares the compression style, if differs return false.
Chris Larsen16ba2b42015-11-02 10:58:31 -08002025 __ Bne(temp1, temp2, &return_false);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002026 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
2027 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
2028 "Expecting 0=compressed, 1=uncompressed");
Chris Larsen16ba2b42015-11-02 10:58:31 -08002029 __ Beqz(temp1, &return_true);
2030
2031 // Don't overwrite input registers
2032 __ Move(TMP, str);
2033 __ Move(temp3, arg);
2034
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002035 // Assertions that must hold in order to compare strings 4 bytes at a time.
Chris Larsen16ba2b42015-11-02 10:58:31 -08002036 DCHECK_ALIGNED(value_offset, 4);
2037 static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded");
2038
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002039 // For string compression, calculate the number of bytes to compare (not chars).
2040 if (mirror::kUseStringCompression) {
2041 // Extract compression flag.
2042 if (IsR2OrNewer()) {
2043 __ Ext(temp2, temp1, 0, 1);
2044 } else {
2045 __ Sll(temp2, temp1, 31);
2046 __ Srl(temp2, temp2, 31);
2047 }
2048 __ Srl(temp1, temp1, 1); // Extract length.
2049 __ Sllv(temp1, temp1, temp2); // Double the byte count if uncompressed.
2050 }
2051
2052 // Loop to compare strings 4 bytes at a time starting at the beginning of the string.
2053 // Ok to do this because strings are zero-padded to kObjectAlignment.
Chris Larsen16ba2b42015-11-02 10:58:31 -08002054 __ Bind(&loop);
2055 __ Lw(out, TMP, value_offset);
2056 __ Lw(temp2, temp3, value_offset);
2057 __ Bne(out, temp2, &return_false);
2058 __ Addiu(TMP, TMP, 4);
2059 __ Addiu(temp3, temp3, 4);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002060 // With string compression, we have compared 4 bytes, otherwise 2 chars.
2061 __ Addiu(temp1, temp1, mirror::kUseStringCompression ? -4 : -2);
Chris Larsen16ba2b42015-11-02 10:58:31 -08002062 __ Bgtz(temp1, &loop);
2063
2064 // Return true and exit the function.
2065 // If loop does not result in returning false, we return true.
2066 __ Bind(&return_true);
2067 __ LoadConst32(out, 1);
2068 __ B(&end);
2069
2070 // Return false and exit the function.
2071 __ Bind(&return_false);
2072 __ LoadConst32(out, 0);
2073 __ Bind(&end);
2074}
2075
Chris Larsencf283da2016-01-19 16:45:35 -08002076static void GenerateStringIndexOf(HInvoke* invoke,
2077 bool start_at_zero,
2078 MipsAssembler* assembler,
Vladimir Marko174b2e22017-10-12 13:34:49 +01002079 CodeGeneratorMIPS* codegen) {
Chris Larsencf283da2016-01-19 16:45:35 -08002080 LocationSummary* locations = invoke->GetLocations();
2081 Register tmp_reg = start_at_zero ? locations->GetTemp(0).AsRegister<Register>() : TMP;
2082
2083 // Note that the null check must have been done earlier.
2084 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
2085
Vladimir Markofb6c90a2016-05-06 15:52:12 +01002086 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
2087 // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
Chris Larsencf283da2016-01-19 16:45:35 -08002088 SlowPathCodeMIPS* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01002089 HInstruction* code_point = invoke->InputAt(1);
2090 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01002091 if (!IsUint<16>(code_point->AsIntConstant()->GetValue())) {
Chris Larsencf283da2016-01-19 16:45:35 -08002092 // Always needs the slow-path. We could directly dispatch to it,
2093 // but this case should be rare, so for simplicity just put the
2094 // full slow-path down and branch unconditionally.
Vladimir Marko174b2e22017-10-12 13:34:49 +01002095 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathMIPS(invoke);
Chris Larsencf283da2016-01-19 16:45:35 -08002096 codegen->AddSlowPath(slow_path);
2097 __ B(slow_path->GetEntryLabel());
2098 __ Bind(slow_path->GetExitLabel());
2099 return;
2100 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002101 } else if (code_point->GetType() != DataType::Type::kUint16) {
Chris Larsencf283da2016-01-19 16:45:35 -08002102 Register char_reg = locations->InAt(1).AsRegister<Register>();
2103 // The "bltu" conditional branch tests to see if the character value
2104 // fits in a valid 16-bit (MIPS halfword) value. If it doesn't then
2105 // the character being searched for, if it exists in the string, is
2106 // encoded using UTF-16 and stored in the string as two (16-bit)
2107 // halfwords. Currently the assembly code used to implement this
2108 // intrinsic doesn't support searching for a character stored as
2109 // two halfwords so we fallback to using the generic implementation
2110 // of indexOf().
2111 __ LoadConst32(tmp_reg, std::numeric_limits<uint16_t>::max());
Vladimir Marko174b2e22017-10-12 13:34:49 +01002112 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathMIPS(invoke);
Chris Larsencf283da2016-01-19 16:45:35 -08002113 codegen->AddSlowPath(slow_path);
2114 __ Bltu(tmp_reg, char_reg, slow_path->GetEntryLabel());
2115 }
2116
2117 if (start_at_zero) {
2118 DCHECK_EQ(tmp_reg, A2);
2119 // Start-index = 0.
2120 __ Clear(tmp_reg);
2121 }
2122
Serban Constantinescufca16662016-07-14 09:21:59 +01002123 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Chris Larsencf283da2016-01-19 16:45:35 -08002124 if (slow_path != nullptr) {
2125 __ Bind(slow_path->GetExitLabel());
2126 }
2127}
2128
2129// int java.lang.String.indexOf(int ch)
2130void IntrinsicLocationsBuilderMIPS::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002131 LocationSummary* locations = new (allocator_) LocationSummary(
2132 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Chris Larsencf283da2016-01-19 16:45:35 -08002133 // We have a hand-crafted assembly stub that follows the runtime
2134 // calling convention. So it's best to align the inputs accordingly.
2135 InvokeRuntimeCallingConvention calling_convention;
2136 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2137 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002138 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002139 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2140
2141 // Need a temp for slow-path codepoint compare, and need to send start-index=0.
2142 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
2143}
2144
2145void IntrinsicCodeGeneratorMIPS::VisitStringIndexOf(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01002146 GenerateStringIndexOf(invoke, /* start_at_zero */ true, GetAssembler(), codegen_);
Chris Larsencf283da2016-01-19 16:45:35 -08002147}
2148
2149// int java.lang.String.indexOf(int ch, int fromIndex)
2150void IntrinsicLocationsBuilderMIPS::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002151 LocationSummary* locations = new (allocator_) LocationSummary(
2152 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Chris Larsencf283da2016-01-19 16:45:35 -08002153 // We have a hand-crafted assembly stub that follows the runtime
2154 // calling convention. So it's best to align the inputs accordingly.
2155 InvokeRuntimeCallingConvention calling_convention;
2156 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2157 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2158 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002159 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002160 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2161
2162 // Need a temp for slow-path codepoint compare.
2163 locations->AddTemp(Location::RequiresRegister());
2164}
2165
2166void IntrinsicCodeGeneratorMIPS::VisitStringIndexOfAfter(HInvoke* invoke) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01002167 GenerateStringIndexOf(invoke, /* start_at_zero */ false, GetAssembler(), codegen_);
Chris Larsencf283da2016-01-19 16:45:35 -08002168}
2169
2170// java.lang.StringFactory.newStringFromBytes(byte[] data, int high, int offset, int byteCount)
2171void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002172 LocationSummary* locations = new (allocator_) LocationSummary(
2173 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Chris Larsencf283da2016-01-19 16:45:35 -08002174 InvokeRuntimeCallingConvention calling_convention;
2175 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2176 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2177 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
2178 locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002179 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002180 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2181}
2182
2183void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromBytes(HInvoke* invoke) {
2184 MipsAssembler* assembler = GetAssembler();
2185 LocationSummary* locations = invoke->GetLocations();
2186
2187 Register byte_array = locations->InAt(0).AsRegister<Register>();
Vladimir Marko174b2e22017-10-12 13:34:49 +01002188 SlowPathCodeMIPS* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathMIPS(invoke);
Chris Larsencf283da2016-01-19 16:45:35 -08002189 codegen_->AddSlowPath(slow_path);
2190 __ Beqz(byte_array, slow_path->GetEntryLabel());
Serban Constantinescufca16662016-07-14 09:21:59 +01002191 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Chris Larsencf283da2016-01-19 16:45:35 -08002192 __ Bind(slow_path->GetExitLabel());
2193}
2194
2195// java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
2196void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromChars(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002197 LocationSummary* locations =
2198 new (allocator_) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Chris Larsencf283da2016-01-19 16:45:35 -08002199 InvokeRuntimeCallingConvention calling_convention;
2200 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2201 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2202 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002203 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002204 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2205}
2206
2207void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromChars(HInvoke* invoke) {
Chris Larsencf283da2016-01-19 16:45:35 -08002208 // No need to emit code checking whether `locations->InAt(2)` is a null
2209 // pointer, as callers of the native method
2210 //
2211 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
2212 //
2213 // all include a null check on `data` before calling that method.
Serban Constantinescufca16662016-07-14 09:21:59 +01002214 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Chris Larsencf283da2016-01-19 16:45:35 -08002215}
2216
2217// java.lang.StringFactory.newStringFromString(String toCopy)
2218void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromString(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002219 LocationSummary* locations = new (allocator_) LocationSummary(
2220 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Chris Larsencf283da2016-01-19 16:45:35 -08002221 InvokeRuntimeCallingConvention calling_convention;
2222 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002223 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002224 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2225}
2226
2227void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromString(HInvoke* invoke) {
2228 MipsAssembler* assembler = GetAssembler();
2229 LocationSummary* locations = invoke->GetLocations();
2230
2231 Register string_to_copy = locations->InAt(0).AsRegister<Register>();
Vladimir Marko174b2e22017-10-12 13:34:49 +01002232 SlowPathCodeMIPS* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathMIPS(invoke);
Chris Larsencf283da2016-01-19 16:45:35 -08002233 codegen_->AddSlowPath(slow_path);
2234 __ Beqz(string_to_copy, slow_path->GetEntryLabel());
Serban Constantinescufca16662016-07-14 09:21:59 +01002235 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc());
Chris Larsencf283da2016-01-19 16:45:35 -08002236 __ Bind(slow_path->GetExitLabel());
2237}
2238
Chris Larsen2714fe62016-02-11 14:23:53 -08002239static void GenIsInfinite(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002240 const DataType::Type type,
Chris Larsen2714fe62016-02-11 14:23:53 -08002241 const bool isR6,
2242 MipsAssembler* assembler) {
2243 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
2244 Register out = locations->Out().AsRegister<Register>();
2245
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002246 DCHECK(type == DataType::Type::kFloat32 || type == DataType::Type::kFloat64);
Chris Larsen2714fe62016-02-11 14:23:53 -08002247
2248 if (isR6) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002249 if (type == DataType::Type::kFloat64) {
Chris Larsen2714fe62016-02-11 14:23:53 -08002250 __ ClassD(FTMP, in);
2251 } else {
2252 __ ClassS(FTMP, in);
2253 }
2254 __ Mfc1(out, FTMP);
2255 __ Andi(out, out, kPositiveInfinity | kNegativeInfinity);
2256 __ Sltu(out, ZERO, out);
2257 } else {
2258 // If one, or more, of the exponent bits is zero, then the number can't be infinite.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002259 if (type == DataType::Type::kFloat64) {
Chris Larsen2714fe62016-02-11 14:23:53 -08002260 __ MoveFromFpuHigh(TMP, in);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002261 __ LoadConst32(AT, High32Bits(kPositiveInfinityDouble));
Chris Larsen2714fe62016-02-11 14:23:53 -08002262 } else {
2263 __ Mfc1(TMP, in);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002264 __ LoadConst32(AT, kPositiveInfinityFloat);
Chris Larsen2714fe62016-02-11 14:23:53 -08002265 }
2266 __ Xor(TMP, TMP, AT);
2267
2268 __ Sll(TMP, TMP, 1);
2269
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002270 if (type == DataType::Type::kFloat64) {
Chris Larsen2714fe62016-02-11 14:23:53 -08002271 __ Mfc1(AT, in);
2272 __ Or(TMP, TMP, AT);
2273 }
2274 // If any of the significand bits are one, then the number is not infinite.
2275 __ Sltiu(out, TMP, 1);
2276 }
2277}
2278
2279// boolean java.lang.Float.isInfinite(float)
2280void IntrinsicLocationsBuilderMIPS::VisitFloatIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002281 CreateFPToIntLocations(allocator_, invoke);
Chris Larsen2714fe62016-02-11 14:23:53 -08002282}
2283
2284void IntrinsicCodeGeneratorMIPS::VisitFloatIsInfinite(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002285 GenIsInfinite(invoke->GetLocations(), DataType::Type::kFloat32, IsR6(), GetAssembler());
Chris Larsen2714fe62016-02-11 14:23:53 -08002286}
2287
2288// boolean java.lang.Double.isInfinite(double)
2289void IntrinsicLocationsBuilderMIPS::VisitDoubleIsInfinite(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002290 CreateFPToIntLocations(allocator_, invoke);
Chris Larsen2714fe62016-02-11 14:23:53 -08002291}
2292
2293void IntrinsicCodeGeneratorMIPS::VisitDoubleIsInfinite(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002294 GenIsInfinite(invoke->GetLocations(), DataType::Type::kFloat64, IsR6(), GetAssembler());
Chris Larsen2714fe62016-02-11 14:23:53 -08002295}
2296
Chris Larsen97759342016-02-16 17:10:40 -08002297static void GenHighestOneBit(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002298 const DataType::Type type,
Chris Larsen97759342016-02-16 17:10:40 -08002299 bool isR6,
2300 MipsAssembler* assembler) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002301 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Chris Larsen97759342016-02-16 17:10:40 -08002302
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002303 if (type == DataType::Type::kInt64) {
Chris Larsen97759342016-02-16 17:10:40 -08002304 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2305 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2306 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
2307 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
2308
2309 if (isR6) {
2310 __ ClzR6(TMP, in_hi);
2311 } else {
2312 __ ClzR2(TMP, in_hi);
2313 }
2314 __ LoadConst32(AT, 0x80000000);
2315 __ Srlv(out_hi, AT, TMP);
2316 __ And(out_hi, out_hi, in_hi);
2317 if (isR6) {
2318 __ ClzR6(TMP, in_lo);
2319 } else {
2320 __ ClzR2(TMP, in_lo);
2321 }
2322 __ Srlv(out_lo, AT, TMP);
2323 __ And(out_lo, out_lo, in_lo);
2324 if (isR6) {
2325 __ Seleqz(out_lo, out_lo, out_hi);
2326 } else {
2327 __ Movn(out_lo, ZERO, out_hi);
2328 }
2329 } else {
2330 Register in = locations->InAt(0).AsRegister<Register>();
2331 Register out = locations->Out().AsRegister<Register>();
2332
2333 if (isR6) {
2334 __ ClzR6(TMP, in);
2335 } else {
2336 __ ClzR2(TMP, in);
2337 }
2338 __ LoadConst32(AT, 0x80000000);
2339 __ Srlv(AT, AT, TMP); // Srlv shifts in the range of [0;31] bits (lower 5 bits of arg).
2340 __ And(out, AT, in); // So this is required for 0 (=shift by 32).
2341 }
2342}
2343
2344// int java.lang.Integer.highestOneBit(int)
2345void IntrinsicLocationsBuilderMIPS::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002346 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen97759342016-02-16 17:10:40 -08002347}
2348
2349void IntrinsicCodeGeneratorMIPS::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002350 GenHighestOneBit(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), GetAssembler());
Chris Larsen97759342016-02-16 17:10:40 -08002351}
2352
2353// long java.lang.Long.highestOneBit(long)
2354void IntrinsicLocationsBuilderMIPS::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002355 CreateIntToIntLocations(allocator_, invoke, Location::kOutputOverlap);
Chris Larsen97759342016-02-16 17:10:40 -08002356}
2357
2358void IntrinsicCodeGeneratorMIPS::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002359 GenHighestOneBit(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler());
Chris Larsen97759342016-02-16 17:10:40 -08002360}
2361
2362static void GenLowestOneBit(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002363 const DataType::Type type,
Chris Larsen97759342016-02-16 17:10:40 -08002364 bool isR6,
2365 MipsAssembler* assembler) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002366 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Chris Larsen97759342016-02-16 17:10:40 -08002367
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002368 if (type == DataType::Type::kInt64) {
Chris Larsen97759342016-02-16 17:10:40 -08002369 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2370 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2371 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
2372 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
2373
2374 __ Subu(TMP, ZERO, in_lo);
2375 __ And(out_lo, TMP, in_lo);
2376 __ Subu(TMP, ZERO, in_hi);
2377 __ And(out_hi, TMP, in_hi);
2378 if (isR6) {
2379 __ Seleqz(out_hi, out_hi, out_lo);
2380 } else {
2381 __ Movn(out_hi, ZERO, out_lo);
2382 }
2383 } else {
2384 Register in = locations->InAt(0).AsRegister<Register>();
2385 Register out = locations->Out().AsRegister<Register>();
2386
2387 __ Subu(TMP, ZERO, in);
2388 __ And(out, TMP, in);
2389 }
2390}
2391
2392// int java.lang.Integer.lowestOneBit(int)
2393void IntrinsicLocationsBuilderMIPS::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002394 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen97759342016-02-16 17:10:40 -08002395}
2396
2397void IntrinsicCodeGeneratorMIPS::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002398 GenLowestOneBit(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), GetAssembler());
Chris Larsen97759342016-02-16 17:10:40 -08002399}
2400
2401// long java.lang.Long.lowestOneBit(long)
2402void IntrinsicLocationsBuilderMIPS::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002403 CreateIntToIntLocations(allocator_, invoke);
Chris Larsen97759342016-02-16 17:10:40 -08002404}
2405
2406void IntrinsicCodeGeneratorMIPS::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002407 GenLowestOneBit(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler());
Chris Larsen97759342016-02-16 17:10:40 -08002408}
2409
Chris Larsenf09d5322016-04-22 12:06:34 -07002410// int java.lang.Math.round(float)
2411void IntrinsicLocationsBuilderMIPS::VisitMathRoundFloat(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002412 LocationSummary* locations =
2413 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsenf09d5322016-04-22 12:06:34 -07002414 locations->SetInAt(0, Location::RequiresFpuRegister());
2415 locations->AddTemp(Location::RequiresFpuRegister());
2416 locations->SetOut(Location::RequiresRegister());
2417}
2418
2419void IntrinsicCodeGeneratorMIPS::VisitMathRoundFloat(HInvoke* invoke) {
2420 LocationSummary* locations = invoke->GetLocations();
2421 MipsAssembler* assembler = GetAssembler();
2422 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
2423 FRegister half = locations->GetTemp(0).AsFpuRegister<FRegister>();
2424 Register out = locations->Out().AsRegister<Register>();
2425
2426 MipsLabel done;
Chris Larsenf09d5322016-04-22 12:06:34 -07002427
Chris Larsenf09d5322016-04-22 12:06:34 -07002428 if (IsR6()) {
Lena Djokicf4e23a82017-05-09 15:43:45 +02002429 // out = floor(in);
2430 //
2431 // if (out != MAX_VALUE && out != MIN_VALUE) {
2432 // TMP = ((in - out) >= 0.5) ? 1 : 0;
2433 // return out += TMP;
2434 // }
2435 // return out;
Chris Larsenf09d5322016-04-22 12:06:34 -07002436
Lena Djokicf4e23a82017-05-09 15:43:45 +02002437 // out = floor(in);
2438 __ FloorWS(FTMP, in);
2439 __ Mfc1(out, FTMP);
Chris Larsenf09d5322016-04-22 12:06:34 -07002440
Lena Djokicf4e23a82017-05-09 15:43:45 +02002441 // if (out != MAX_VALUE && out != MIN_VALUE)
2442 __ Addiu(TMP, out, 1);
2443 __ Aui(TMP, TMP, 0x8000); // TMP = out + 0x8000 0001
2444 // or out - 0x7FFF FFFF.
2445 // IOW, TMP = 1 if out = Int.MIN_VALUE
2446 // or TMP = 0 if out = Int.MAX_VALUE.
2447 __ Srl(TMP, TMP, 1); // TMP = 0 if out = Int.MIN_VALUE
2448 // or out = Int.MAX_VALUE.
2449 __ Beqz(TMP, &done);
Chris Larsenf09d5322016-04-22 12:06:34 -07002450
Lena Djokicf4e23a82017-05-09 15:43:45 +02002451 // TMP = (0.5f <= (in - out)) ? -1 : 0;
2452 __ Cvtsw(FTMP, FTMP); // Convert output of floor.w.s back to "float".
2453 __ LoadConst32(AT, bit_cast<int32_t, float>(0.5f));
2454 __ SubS(FTMP, in, FTMP);
2455 __ Mtc1(AT, half);
Chris Larsenf09d5322016-04-22 12:06:34 -07002456
Chris Larsenf09d5322016-04-22 12:06:34 -07002457 __ CmpLeS(FTMP, half, FTMP);
Chris Larsen07f712f2016-06-10 16:06:02 -07002458 __ Mfc1(TMP, FTMP);
Lena Djokicf4e23a82017-05-09 15:43:45 +02002459
2460 // Return out -= TMP.
2461 __ Subu(out, out, TMP);
Chris Larsenf09d5322016-04-22 12:06:34 -07002462 } else {
Lena Djokicf4e23a82017-05-09 15:43:45 +02002463 // if (in.isNaN) {
2464 // return 0;
2465 // }
2466 //
2467 // out = floor.w.s(in);
2468 //
2469 // /*
2470 // * This "if" statement is only needed for the pre-R6 version of floor.w.s
2471 // * which outputs Integer.MAX_VALUE for negative numbers with magnitudes
2472 // * too large to fit in a 32-bit integer.
2473 // */
2474 // if (out == Integer.MAX_VALUE) {
2475 // TMP = (in < 0.0f) ? 1 : 0;
2476 // /*
2477 // * If TMP is 1, then adding it to out will wrap its value from
2478 // * Integer.MAX_VALUE to Integer.MIN_VALUE.
2479 // */
2480 // return out += TMP;
2481 // }
2482 //
2483 // /*
2484 // * For negative values not handled by the previous "if" statement the
2485 // * test here will correctly set the value of TMP.
2486 // */
2487 // TMP = ((in - out) >= 0.5f) ? 1 : 0;
2488 // return out += TMP;
2489
2490 MipsLabel finite;
2491 MipsLabel add;
2492
2493 // Test for NaN.
2494 __ CunS(in, in);
2495
2496 // Return zero for NaN.
2497 __ Move(out, ZERO);
2498 __ Bc1t(&done);
2499
2500 // out = floor(in);
2501 __ FloorWS(FTMP, in);
2502 __ Mfc1(out, FTMP);
2503
2504 __ LoadConst32(TMP, -1);
2505
2506 // TMP = (out = java.lang.Integer.MAX_VALUE) ? -1 : 0;
2507 __ LoadConst32(AT, std::numeric_limits<int32_t>::max());
2508 __ Bne(AT, out, &finite);
2509
2510 __ Mtc1(ZERO, FTMP);
2511 __ ColtS(in, FTMP);
2512
2513 __ B(&add);
2514
2515 __ Bind(&finite);
2516
2517 // TMP = (0.5f <= (in - out)) ? -1 : 0;
2518 __ Cvtsw(FTMP, FTMP); // Convert output of floor.w.s back to "float".
2519 __ LoadConst32(AT, bit_cast<int32_t, float>(0.5f));
2520 __ SubS(FTMP, in, FTMP);
2521 __ Mtc1(AT, half);
Chris Larsenf09d5322016-04-22 12:06:34 -07002522 __ ColeS(half, FTMP);
Chris Larsenf09d5322016-04-22 12:06:34 -07002523
Lena Djokicf4e23a82017-05-09 15:43:45 +02002524 __ Bind(&add);
Chris Larsenf09d5322016-04-22 12:06:34 -07002525
Chris Larsenf09d5322016-04-22 12:06:34 -07002526 __ Movf(TMP, ZERO);
Lena Djokicf4e23a82017-05-09 15:43:45 +02002527
2528 // Return out -= TMP.
2529 __ Subu(out, out, TMP);
Chris Larsenf09d5322016-04-22 12:06:34 -07002530 }
Chris Larsenf09d5322016-04-22 12:06:34 -07002531 __ Bind(&done);
2532}
2533
Chris Larsen692235e2016-11-21 16:04:53 -08002534// void java.lang.String.getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
2535void IntrinsicLocationsBuilderMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002536 LocationSummary* locations =
2537 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Chris Larsen692235e2016-11-21 16:04:53 -08002538 locations->SetInAt(0, Location::RequiresRegister());
2539 locations->SetInAt(1, Location::RequiresRegister());
2540 locations->SetInAt(2, Location::RequiresRegister());
2541 locations->SetInAt(3, Location::RequiresRegister());
2542 locations->SetInAt(4, Location::RequiresRegister());
2543
Chris Larsenfe4ff442017-03-23 11:25:12 -07002544 locations->AddTemp(Location::RequiresRegister());
2545 locations->AddTemp(Location::RequiresRegister());
2546 locations->AddTemp(Location::RequiresRegister());
Chris Larsen692235e2016-11-21 16:04:53 -08002547}
2548
2549void IntrinsicCodeGeneratorMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2550 MipsAssembler* assembler = GetAssembler();
2551 LocationSummary* locations = invoke->GetLocations();
2552
2553 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002554 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Chris Larsen692235e2016-11-21 16:04:53 -08002555 DCHECK_EQ(char_size, 2u);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002556 const size_t char_shift = DataType::SizeShift(DataType::Type::kUint16);
Chris Larsen692235e2016-11-21 16:04:53 -08002557
2558 Register srcObj = locations->InAt(0).AsRegister<Register>();
2559 Register srcBegin = locations->InAt(1).AsRegister<Register>();
2560 Register srcEnd = locations->InAt(2).AsRegister<Register>();
2561 Register dstObj = locations->InAt(3).AsRegister<Register>();
2562 Register dstBegin = locations->InAt(4).AsRegister<Register>();
2563
2564 Register dstPtr = locations->GetTemp(0).AsRegister<Register>();
Chris Larsen692235e2016-11-21 16:04:53 -08002565 Register srcPtr = locations->GetTemp(1).AsRegister<Register>();
Chris Larsen692235e2016-11-21 16:04:53 -08002566 Register numChrs = locations->GetTemp(2).AsRegister<Register>();
Chris Larsen692235e2016-11-21 16:04:53 -08002567
2568 MipsLabel done;
Chris Larsenfe4ff442017-03-23 11:25:12 -07002569 MipsLabel loop;
Chris Larsen692235e2016-11-21 16:04:53 -08002570
2571 // Location of data in char array buffer.
2572 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2573
2574 // Get offset of value field within a string object.
2575 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
2576
2577 __ Beq(srcEnd, srcBegin, &done); // No characters to move.
2578
2579 // Calculate number of characters to be copied.
2580 __ Subu(numChrs, srcEnd, srcBegin);
2581
2582 // Calculate destination address.
2583 __ Addiu(dstPtr, dstObj, data_offset);
Chris Larsencd0295d2017-03-31 15:26:54 -07002584 __ ShiftAndAdd(dstPtr, dstBegin, dstPtr, char_shift);
Chris Larsen692235e2016-11-21 16:04:53 -08002585
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002586 if (mirror::kUseStringCompression) {
2587 MipsLabel uncompressed_copy, compressed_loop;
2588 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2589 // Load count field and extract compression flag.
2590 __ LoadFromOffset(kLoadWord, TMP, srcObj, count_offset);
2591 __ Sll(TMP, TMP, 31);
2592
Chris Larsenfe4ff442017-03-23 11:25:12 -07002593 // If string is uncompressed, use uncompressed path.
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002594 __ Bnez(TMP, &uncompressed_copy);
2595
2596 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2597 __ Addu(srcPtr, srcObj, srcBegin);
2598 __ Bind(&compressed_loop);
2599 __ LoadFromOffset(kLoadUnsignedByte, TMP, srcPtr, value_offset);
2600 __ StoreToOffset(kStoreHalfword, TMP, dstPtr, 0);
2601 __ Addiu(numChrs, numChrs, -1);
2602 __ Addiu(srcPtr, srcPtr, 1);
2603 __ Addiu(dstPtr, dstPtr, 2);
2604 __ Bnez(numChrs, &compressed_loop);
2605
2606 __ B(&done);
2607 __ Bind(&uncompressed_copy);
2608 }
2609
Chris Larsen692235e2016-11-21 16:04:53 -08002610 // Calculate source address.
2611 __ Addiu(srcPtr, srcObj, value_offset);
Chris Larsencd0295d2017-03-31 15:26:54 -07002612 __ ShiftAndAdd(srcPtr, srcBegin, srcPtr, char_shift);
Chris Larsen692235e2016-11-21 16:04:53 -08002613
Chris Larsenfe4ff442017-03-23 11:25:12 -07002614 __ Bind(&loop);
2615 __ Lh(AT, srcPtr, 0);
2616 __ Addiu(numChrs, numChrs, -1);
2617 __ Addiu(srcPtr, srcPtr, char_size);
2618 __ Sh(AT, dstPtr, 0);
2619 __ Addiu(dstPtr, dstPtr, char_size);
2620 __ Bnez(numChrs, &loop);
Chris Larsen692235e2016-11-21 16:04:53 -08002621
2622 __ Bind(&done);
2623}
2624
Vladimir Markoca6fff82017-10-03 14:49:14 +01002625static void CreateFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
2626 LocationSummary* locations =
2627 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002628 InvokeRuntimeCallingConvention calling_convention;
2629
2630 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002631 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kFloat64));
Chris Larsenb9005fa2017-03-24 12:11:54 -07002632}
2633
Vladimir Markoca6fff82017-10-03 14:49:14 +01002634static void CreateFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
2635 LocationSummary* locations =
2636 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002637 InvokeRuntimeCallingConvention calling_convention;
2638
2639 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
2640 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002641 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kFloat64));
Chris Larsenb9005fa2017-03-24 12:11:54 -07002642}
2643
2644static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorMIPS* codegen, QuickEntrypointEnum entry) {
2645 LocationSummary* locations = invoke->GetLocations();
2646 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
2647 DCHECK_EQ(in, F12);
2648 FRegister out = locations->Out().AsFpuRegister<FRegister>();
2649 DCHECK_EQ(out, F0);
2650
2651 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2652}
2653
2654static void GenFPFPToFPCall(HInvoke* invoke,
2655 CodeGeneratorMIPS* codegen,
2656 QuickEntrypointEnum entry) {
2657 LocationSummary* locations = invoke->GetLocations();
2658 FRegister in0 = locations->InAt(0).AsFpuRegister<FRegister>();
2659 DCHECK_EQ(in0, F12);
2660 FRegister in1 = locations->InAt(1).AsFpuRegister<FRegister>();
2661 DCHECK_EQ(in1, F14);
2662 FRegister out = locations->Out().AsFpuRegister<FRegister>();
2663 DCHECK_EQ(out, F0);
2664
2665 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2666}
2667
2668// static double java.lang.Math.cos(double a)
2669void IntrinsicLocationsBuilderMIPS::VisitMathCos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002670 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002671}
2672
2673void IntrinsicCodeGeneratorMIPS::VisitMathCos(HInvoke* invoke) {
2674 GenFPToFPCall(invoke, codegen_, kQuickCos);
2675}
2676
2677// static double java.lang.Math.sin(double a)
2678void IntrinsicLocationsBuilderMIPS::VisitMathSin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002679 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002680}
2681
2682void IntrinsicCodeGeneratorMIPS::VisitMathSin(HInvoke* invoke) {
2683 GenFPToFPCall(invoke, codegen_, kQuickSin);
2684}
2685
2686// static double java.lang.Math.acos(double a)
2687void IntrinsicLocationsBuilderMIPS::VisitMathAcos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002688 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002689}
2690
2691void IntrinsicCodeGeneratorMIPS::VisitMathAcos(HInvoke* invoke) {
2692 GenFPToFPCall(invoke, codegen_, kQuickAcos);
2693}
2694
2695// static double java.lang.Math.asin(double a)
2696void IntrinsicLocationsBuilderMIPS::VisitMathAsin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002697 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002698}
2699
2700void IntrinsicCodeGeneratorMIPS::VisitMathAsin(HInvoke* invoke) {
2701 GenFPToFPCall(invoke, codegen_, kQuickAsin);
2702}
2703
2704// static double java.lang.Math.atan(double a)
2705void IntrinsicLocationsBuilderMIPS::VisitMathAtan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002706 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002707}
2708
2709void IntrinsicCodeGeneratorMIPS::VisitMathAtan(HInvoke* invoke) {
2710 GenFPToFPCall(invoke, codegen_, kQuickAtan);
2711}
2712
2713// static double java.lang.Math.atan2(double y, double x)
2714void IntrinsicLocationsBuilderMIPS::VisitMathAtan2(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002715 CreateFPFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002716}
2717
2718void IntrinsicCodeGeneratorMIPS::VisitMathAtan2(HInvoke* invoke) {
2719 GenFPFPToFPCall(invoke, codegen_, kQuickAtan2);
2720}
2721
Vladimir Marko4d179872018-01-19 14:50:10 +00002722// static double java.lang.Math.pow(double y, double x)
2723void IntrinsicLocationsBuilderMIPS::VisitMathPow(HInvoke* invoke) {
2724 CreateFPFPToFPCallLocations(allocator_, invoke);
2725}
2726
2727void IntrinsicCodeGeneratorMIPS::VisitMathPow(HInvoke* invoke) {
2728 GenFPFPToFPCall(invoke, codegen_, kQuickPow);
2729}
2730
Chris Larsenb9005fa2017-03-24 12:11:54 -07002731// static double java.lang.Math.cbrt(double a)
2732void IntrinsicLocationsBuilderMIPS::VisitMathCbrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002733 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002734}
2735
2736void IntrinsicCodeGeneratorMIPS::VisitMathCbrt(HInvoke* invoke) {
2737 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
2738}
2739
2740// static double java.lang.Math.cosh(double x)
2741void IntrinsicLocationsBuilderMIPS::VisitMathCosh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002742 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002743}
2744
2745void IntrinsicCodeGeneratorMIPS::VisitMathCosh(HInvoke* invoke) {
2746 GenFPToFPCall(invoke, codegen_, kQuickCosh);
2747}
2748
2749// static double java.lang.Math.exp(double a)
2750void IntrinsicLocationsBuilderMIPS::VisitMathExp(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002751 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002752}
2753
2754void IntrinsicCodeGeneratorMIPS::VisitMathExp(HInvoke* invoke) {
2755 GenFPToFPCall(invoke, codegen_, kQuickExp);
2756}
2757
2758// static double java.lang.Math.expm1(double x)
2759void IntrinsicLocationsBuilderMIPS::VisitMathExpm1(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002760 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002761}
2762
2763void IntrinsicCodeGeneratorMIPS::VisitMathExpm1(HInvoke* invoke) {
2764 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
2765}
2766
2767// static double java.lang.Math.hypot(double x, double y)
2768void IntrinsicLocationsBuilderMIPS::VisitMathHypot(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002769 CreateFPFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002770}
2771
2772void IntrinsicCodeGeneratorMIPS::VisitMathHypot(HInvoke* invoke) {
2773 GenFPFPToFPCall(invoke, codegen_, kQuickHypot);
2774}
2775
2776// static double java.lang.Math.log(double a)
2777void IntrinsicLocationsBuilderMIPS::VisitMathLog(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002778 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002779}
2780
2781void IntrinsicCodeGeneratorMIPS::VisitMathLog(HInvoke* invoke) {
2782 GenFPToFPCall(invoke, codegen_, kQuickLog);
2783}
2784
2785// static double java.lang.Math.log10(double x)
2786void IntrinsicLocationsBuilderMIPS::VisitMathLog10(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002787 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002788}
2789
2790void IntrinsicCodeGeneratorMIPS::VisitMathLog10(HInvoke* invoke) {
2791 GenFPToFPCall(invoke, codegen_, kQuickLog10);
2792}
2793
2794// static double java.lang.Math.nextAfter(double start, double direction)
2795void IntrinsicLocationsBuilderMIPS::VisitMathNextAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002796 CreateFPFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002797}
2798
2799void IntrinsicCodeGeneratorMIPS::VisitMathNextAfter(HInvoke* invoke) {
2800 GenFPFPToFPCall(invoke, codegen_, kQuickNextAfter);
2801}
2802
2803// static double java.lang.Math.sinh(double x)
2804void IntrinsicLocationsBuilderMIPS::VisitMathSinh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002805 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002806}
2807
2808void IntrinsicCodeGeneratorMIPS::VisitMathSinh(HInvoke* invoke) {
2809 GenFPToFPCall(invoke, codegen_, kQuickSinh);
2810}
2811
2812// static double java.lang.Math.tan(double a)
2813void IntrinsicLocationsBuilderMIPS::VisitMathTan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002814 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002815}
2816
2817void IntrinsicCodeGeneratorMIPS::VisitMathTan(HInvoke* invoke) {
2818 GenFPToFPCall(invoke, codegen_, kQuickTan);
2819}
2820
2821// static double java.lang.Math.tanh(double x)
2822void IntrinsicLocationsBuilderMIPS::VisitMathTanh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002823 CreateFPToFPCallLocations(allocator_, invoke);
Chris Larsenb9005fa2017-03-24 12:11:54 -07002824}
2825
2826void IntrinsicCodeGeneratorMIPS::VisitMathTanh(HInvoke* invoke) {
2827 GenFPToFPCall(invoke, codegen_, kQuickTanh);
2828}
2829
Chris Larsen2f6ad9d2017-03-23 15:37:03 -07002830// static void java.lang.System.arraycopy(Object src, int srcPos,
2831// Object dest, int destPos,
2832// int length)
2833void IntrinsicLocationsBuilderMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) {
2834 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2835 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2836 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2837
2838 // As long as we are checking, we might as well check to see if the src and dest
2839 // positions are >= 0.
2840 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2841 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2842 // We will have to fail anyways.
2843 return;
2844 }
2845
2846 // And since we are already checking, check the length too.
2847 if (length != nullptr) {
2848 int32_t len = length->GetValue();
2849 if (len < 0) {
2850 // Just call as normal.
2851 return;
2852 }
2853 }
2854
2855 // Okay, it is safe to generate inline code.
2856 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002857 new (allocator_) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
Chris Larsen2f6ad9d2017-03-23 15:37:03 -07002858 // arraycopy(Object src, int srcPos, Object dest, int destPos, int length).
2859 locations->SetInAt(0, Location::RequiresRegister());
2860 locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
2861 locations->SetInAt(2, Location::RequiresRegister());
2862 locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3)));
2863 locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4)));
2864
2865 locations->AddTemp(Location::RequiresRegister());
2866 locations->AddTemp(Location::RequiresRegister());
2867 locations->AddTemp(Location::RequiresRegister());
2868}
2869
2870// Utility routine to verify that "length(input) - pos >= length"
2871static void EnoughItems(MipsAssembler* assembler,
2872 Register length_input_minus_pos,
2873 Location length,
2874 SlowPathCodeMIPS* slow_path) {
2875 if (length.IsConstant()) {
2876 int32_t length_constant = length.GetConstant()->AsIntConstant()->GetValue();
2877
2878 if (IsInt<16>(length_constant)) {
2879 __ Slti(TMP, length_input_minus_pos, length_constant);
2880 __ Bnez(TMP, slow_path->GetEntryLabel());
2881 } else {
2882 __ LoadConst32(TMP, length_constant);
2883 __ Blt(length_input_minus_pos, TMP, slow_path->GetEntryLabel());
2884 }
2885 } else {
2886 __ Blt(length_input_minus_pos, length.AsRegister<Register>(), slow_path->GetEntryLabel());
2887 }
2888}
2889
2890static void CheckPosition(MipsAssembler* assembler,
2891 Location pos,
2892 Register input,
2893 Location length,
2894 SlowPathCodeMIPS* slow_path,
2895 bool length_is_input_length = false) {
2896 // Where is the length in the Array?
2897 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
2898
2899 // Calculate length(input) - pos.
2900 if (pos.IsConstant()) {
2901 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
2902 if (pos_const == 0) {
2903 if (!length_is_input_length) {
2904 // Check that length(input) >= length.
2905 __ LoadFromOffset(kLoadWord, AT, input, length_offset);
2906 EnoughItems(assembler, AT, length, slow_path);
2907 }
2908 } else {
2909 // Check that (length(input) - pos) >= zero.
2910 __ LoadFromOffset(kLoadWord, AT, input, length_offset);
2911 DCHECK_GT(pos_const, 0);
2912 __ Addiu32(AT, AT, -pos_const, TMP);
2913 __ Bltz(AT, slow_path->GetEntryLabel());
2914
2915 // Verify that (length(input) - pos) >= length.
2916 EnoughItems(assembler, AT, length, slow_path);
2917 }
2918 } else if (length_is_input_length) {
2919 // The only way the copy can succeed is if pos is zero.
2920 Register pos_reg = pos.AsRegister<Register>();
2921 __ Bnez(pos_reg, slow_path->GetEntryLabel());
2922 } else {
2923 // Verify that pos >= 0.
2924 Register pos_reg = pos.AsRegister<Register>();
2925 __ Bltz(pos_reg, slow_path->GetEntryLabel());
2926
2927 // Check that (length(input) - pos) >= zero.
2928 __ LoadFromOffset(kLoadWord, AT, input, length_offset);
2929 __ Subu(AT, AT, pos_reg);
2930 __ Bltz(AT, slow_path->GetEntryLabel());
2931
2932 // Verify that (length(input) - pos) >= length.
2933 EnoughItems(assembler, AT, length, slow_path);
2934 }
2935}
2936
2937void IntrinsicCodeGeneratorMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) {
2938 MipsAssembler* assembler = GetAssembler();
2939 LocationSummary* locations = invoke->GetLocations();
2940
2941 Register src = locations->InAt(0).AsRegister<Register>();
2942 Location src_pos = locations->InAt(1);
2943 Register dest = locations->InAt(2).AsRegister<Register>();
2944 Location dest_pos = locations->InAt(3);
2945 Location length = locations->InAt(4);
2946
2947 MipsLabel loop;
2948
2949 Register dest_base = locations->GetTemp(0).AsRegister<Register>();
2950 Register src_base = locations->GetTemp(1).AsRegister<Register>();
2951 Register count = locations->GetTemp(2).AsRegister<Register>();
2952
Vladimir Marko174b2e22017-10-12 13:34:49 +01002953 SlowPathCodeMIPS* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathMIPS(invoke);
Chris Larsen2f6ad9d2017-03-23 15:37:03 -07002954 codegen_->AddSlowPath(slow_path);
2955
2956 // Bail out if the source and destination are the same (to handle overlap).
2957 __ Beq(src, dest, slow_path->GetEntryLabel());
2958
2959 // Bail out if the source is null.
2960 __ Beqz(src, slow_path->GetEntryLabel());
2961
2962 // Bail out if the destination is null.
2963 __ Beqz(dest, slow_path->GetEntryLabel());
2964
2965 // Load length into register for count.
2966 if (length.IsConstant()) {
2967 __ LoadConst32(count, length.GetConstant()->AsIntConstant()->GetValue());
2968 } else {
2969 // If the length is negative, bail out.
2970 // We have already checked in the LocationsBuilder for the constant case.
2971 __ Bltz(length.AsRegister<Register>(), slow_path->GetEntryLabel());
2972
2973 __ Move(count, length.AsRegister<Register>());
2974 }
2975
2976 // Validity checks: source.
2977 CheckPosition(assembler, src_pos, src, Location::RegisterLocation(count), slow_path);
2978
2979 // Validity checks: dest.
2980 CheckPosition(assembler, dest_pos, dest, Location::RegisterLocation(count), slow_path);
2981
2982 // If count is zero, we're done.
2983 __ Beqz(count, slow_path->GetExitLabel());
2984
2985 // Okay, everything checks out. Finally time to do the copy.
2986 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002987 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Chris Larsen2f6ad9d2017-03-23 15:37:03 -07002988 DCHECK_EQ(char_size, 2u);
2989
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002990 const size_t char_shift = DataType::SizeShift(DataType::Type::kUint16);
Chris Larsen2f6ad9d2017-03-23 15:37:03 -07002991
2992 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2993
2994 // Calculate source and destination addresses.
2995 if (src_pos.IsConstant()) {
2996 int32_t src_pos_const = src_pos.GetConstant()->AsIntConstant()->GetValue();
2997
2998 __ Addiu32(src_base, src, data_offset + char_size * src_pos_const, TMP);
2999 } else {
3000 __ Addiu32(src_base, src, data_offset, TMP);
3001 __ ShiftAndAdd(src_base, src_pos.AsRegister<Register>(), src_base, char_shift);
3002 }
3003 if (dest_pos.IsConstant()) {
3004 int32_t dest_pos_const = dest_pos.GetConstant()->AsIntConstant()->GetValue();
3005
3006 __ Addiu32(dest_base, dest, data_offset + char_size * dest_pos_const, TMP);
3007 } else {
3008 __ Addiu32(dest_base, dest, data_offset, TMP);
3009 __ ShiftAndAdd(dest_base, dest_pos.AsRegister<Register>(), dest_base, char_shift);
3010 }
3011
3012 __ Bind(&loop);
3013 __ Lh(TMP, src_base, 0);
3014 __ Addiu(src_base, src_base, char_size);
3015 __ Addiu(count, count, -1);
3016 __ Sh(TMP, dest_base, 0);
3017 __ Addiu(dest_base, dest_base, char_size);
3018 __ Bnez(count, &loop);
3019
3020 __ Bind(slow_path->GetExitLabel());
3021}
3022
Chris Larsen5633ce72017-04-10 15:47:40 -07003023// long java.lang.Integer.valueOf(long)
3024void IntrinsicLocationsBuilderMIPS::VisitIntegerValueOf(HInvoke* invoke) {
3025 InvokeRuntimeCallingConvention calling_convention;
3026 IntrinsicVisitor::ComputeIntegerValueOfLocations(
3027 invoke,
3028 codegen_,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003029 calling_convention.GetReturnLocation(DataType::Type::kReference),
Chris Larsen5633ce72017-04-10 15:47:40 -07003030 Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
3031}
3032
3033void IntrinsicCodeGeneratorMIPS::VisitIntegerValueOf(HInvoke* invoke) {
3034 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
3035 LocationSummary* locations = invoke->GetLocations();
3036 MipsAssembler* assembler = GetAssembler();
3037 InstructionCodeGeneratorMIPS* icodegen =
3038 down_cast<InstructionCodeGeneratorMIPS*>(codegen_->GetInstructionVisitor());
3039
3040 Register out = locations->Out().AsRegister<Register>();
3041 InvokeRuntimeCallingConvention calling_convention;
3042 if (invoke->InputAt(0)->IsConstant()) {
3043 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
3044 if (value >= info.low && value <= info.high) {
3045 // Just embed the j.l.Integer in the code.
3046 ScopedObjectAccess soa(Thread::Current());
3047 mirror::Object* boxed = info.cache->Get(value + (-info.low));
3048 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
3049 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
3050 __ LoadConst32(out, address);
3051 } else {
3052 // Allocate and initialize a new j.l.Integer.
3053 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
3054 // JIT object table.
3055 uint32_t address =
3056 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
3057 __ LoadConst32(calling_convention.GetRegisterAt(0), address);
3058 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3059 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3060 __ StoreConstToOffset(kStoreWord, value, out, info.value_offset, TMP);
3061 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3062 // one.
3063 icodegen->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3064 }
3065 } else {
3066 Register in = locations->InAt(0).AsRegister<Register>();
3067 MipsLabel allocate, done;
3068 int32_t count = static_cast<uint32_t>(info.high) - info.low + 1;
3069
3070 // Is (info.low <= in) && (in <= info.high)?
3071 __ Addiu32(out, in, -info.low);
3072 // As unsigned quantities is out < (info.high - info.low + 1)?
3073 if (IsInt<16>(count)) {
3074 __ Sltiu(AT, out, count);
3075 } else {
3076 __ LoadConst32(AT, count);
3077 __ Sltu(AT, out, AT);
3078 }
3079 // Branch if out >= (info.high - info.low + 1).
3080 // This means that "in" is outside of the range [info.low, info.high].
3081 __ Beqz(AT, &allocate);
3082
3083 // If the value is within the bounds, load the j.l.Integer directly from the array.
3084 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3085 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
3086 __ LoadConst32(TMP, data_offset + address);
3087 __ ShiftAndAdd(out, out, TMP, TIMES_4);
3088 __ Lw(out, out, 0);
3089 __ MaybeUnpoisonHeapReference(out);
3090 __ B(&done);
3091
3092 __ Bind(&allocate);
3093 // Otherwise allocate and initialize a new j.l.Integer.
3094 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
3095 __ LoadConst32(calling_convention.GetRegisterAt(0), address);
3096 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3097 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3098 __ StoreToOffset(kStoreWord, in, out, info.value_offset);
3099 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3100 // one.
3101 icodegen->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3102 __ Bind(&done);
3103 }
3104}
3105
Chris Larsenb065b032017-11-02 12:13:20 -07003106// static boolean java.lang.Thread.interrupted()
3107void IntrinsicLocationsBuilderMIPS::VisitThreadInterrupted(HInvoke* invoke) {
3108 LocationSummary* locations =
3109 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
3110 locations->SetOut(Location::RequiresRegister());
3111}
3112
3113void IntrinsicCodeGeneratorMIPS::VisitThreadInterrupted(HInvoke* invoke) {
3114 MipsAssembler* assembler = GetAssembler();
3115 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
3116 int32_t offset = Thread::InterruptedOffset<kMipsPointerSize>().Int32Value();
3117 __ LoadFromOffset(kLoadWord, out, TR, offset);
3118 MipsLabel done;
3119 __ Beqz(out, &done);
3120 __ Sync(0);
3121 __ StoreToOffset(kStoreWord, ZERO, TR, offset);
3122 __ Sync(0);
3123 __ Bind(&done);
3124}
3125
Chris Larsen2714fe62016-02-11 14:23:53 -08003126// Unimplemented intrinsics.
3127
Aart Bik2f9fcc92016-03-01 15:16:54 -08003128UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil)
3129UNIMPLEMENTED_INTRINSIC(MIPS, MathFloor)
3130UNIMPLEMENTED_INTRINSIC(MIPS, MathRint)
3131UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble)
Alexey Frunze15958152017-02-09 19:08:30 -08003132UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLongVolatile);
3133UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongVolatile);
Aart Bik2f9fcc92016-03-01 15:16:54 -08003134UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
Chris Larsen701566a2015-10-27 15:29:13 -07003135
Aart Bik2f9fcc92016-03-01 15:16:54 -08003136UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
Aart Bik2f9fcc92016-03-01 15:16:54 -08003137UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
Aart Bik3f67e692016-01-15 14:35:12 -08003138
Aart Bikff7d89c2016-11-07 08:49:28 -08003139UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
3140UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003141UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferAppend);
3142UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferLength);
3143UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferToString);
3144UNIMPLEMENTED_INTRINSIC(MIPS, StringBuilderAppend);
3145UNIMPLEMENTED_INTRINSIC(MIPS, StringBuilderLength);
3146UNIMPLEMENTED_INTRINSIC(MIPS, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003147
Aart Bik0e54c012016-03-04 12:08:31 -08003148// 1.8.
3149UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddInt)
3150UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddLong)
3151UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetInt)
3152UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong)
3153UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject)
Chris Larsen701566a2015-10-27 15:29:13 -07003154
Aart Bik0e54c012016-03-04 12:08:31 -08003155UNREACHABLE_INTRINSICS(MIPS)
Chris Larsen2714fe62016-02-11 14:23:53 -08003156
Chris Larsen701566a2015-10-27 15:29:13 -07003157#undef __
3158
3159} // namespace mips
3160} // namespace art