blob: 8847256532345799a997534be233d2c4c935ae3c [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)
Chris Larsen5633ce72017-04-10 15:47:40 -070038 : codegen_(codegen), arena_(codegen->GetGraph()->GetArena()) {
Chris Larsen701566a2015-10-27 15:29:13 -070039}
40
41MipsAssembler* IntrinsicCodeGeneratorMIPS::GetAssembler() {
42 return reinterpret_cast<MipsAssembler*>(codegen_->GetAssembler());
43}
44
45ArenaAllocator* IntrinsicCodeGeneratorMIPS::GetAllocator() {
46 return codegen_->GetGraph()->GetArena();
47}
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
Chris Larsen3f8bf652015-10-28 10:08:56 -0700155static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
156 LocationSummary* locations = new (arena) LocationSummary(invoke,
157 LocationSummary::kNoCall,
158 kIntrinsified);
159 locations->SetInAt(0, Location::RequiresFpuRegister());
160 locations->SetOut(Location::RequiresRegister());
161}
162
163static void MoveFPToInt(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
164 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
165
166 if (is64bit) {
167 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
168 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
169
170 __ Mfc1(out_lo, in);
Alexey Frunzebb9863a2016-01-11 15:51:16 -0800171 __ MoveFromFpuHigh(out_hi, in);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700172 } else {
173 Register out = locations->Out().AsRegister<Register>();
174
175 __ Mfc1(out, in);
176 }
177}
178
179// long java.lang.Double.doubleToRawLongBits(double)
180void IntrinsicLocationsBuilderMIPS::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
181 CreateFPToIntLocations(arena_, invoke);
182}
183
184void IntrinsicCodeGeneratorMIPS::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000185 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700186}
187
188// int java.lang.Float.floatToRawIntBits(float)
189void IntrinsicLocationsBuilderMIPS::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
190 CreateFPToIntLocations(arena_, invoke);
191}
192
193void IntrinsicCodeGeneratorMIPS::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000194 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700195}
196
197static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
198 LocationSummary* locations = new (arena) LocationSummary(invoke,
199 LocationSummary::kNoCall,
200 kIntrinsified);
201 locations->SetInAt(0, Location::RequiresRegister());
202 locations->SetOut(Location::RequiresFpuRegister());
203}
204
205static void MoveIntToFP(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
206 FRegister out = locations->Out().AsFpuRegister<FRegister>();
207
208 if (is64bit) {
209 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
210 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
211
212 __ Mtc1(in_lo, out);
Alexey Frunzebb9863a2016-01-11 15:51:16 -0800213 __ MoveToFpuHigh(in_hi, out);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700214 } else {
215 Register in = locations->InAt(0).AsRegister<Register>();
216
217 __ Mtc1(in, out);
218 }
219}
220
221// double java.lang.Double.longBitsToDouble(long)
222void IntrinsicLocationsBuilderMIPS::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
223 CreateIntToFPLocations(arena_, invoke);
224}
225
226void IntrinsicCodeGeneratorMIPS::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000227 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700228}
229
230// float java.lang.Float.intBitsToFloat(int)
231void IntrinsicLocationsBuilderMIPS::VisitFloatIntBitsToFloat(HInvoke* invoke) {
232 CreateIntToFPLocations(arena_, invoke);
233}
234
235void IntrinsicCodeGeneratorMIPS::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +0000236 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700237}
238
Chris Larsen86829602015-11-18 12:27:52 -0800239static void CreateIntToIntLocations(ArenaAllocator* arena,
240 HInvoke* invoke,
241 Location::OutputOverlap overlaps = Location::kNoOutputOverlap) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700242 LocationSummary* locations = new (arena) LocationSummary(invoke,
243 LocationSummary::kNoCall,
244 kIntrinsified);
245 locations->SetInAt(0, Location::RequiresRegister());
Chris Larsen86829602015-11-18 12:27:52 -0800246 locations->SetOut(Location::RequiresRegister(), overlaps);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700247}
248
Chris Larsen70014c82015-11-18 12:26:08 -0800249static void GenReverse(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100250 DataType::Type type,
Chris Larsen70014c82015-11-18 12:26:08 -0800251 bool isR2OrNewer,
252 bool isR6,
253 bool reverseBits,
254 MipsAssembler* assembler) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100255 DCHECK(type == DataType::Type::kInt16 ||
256 type == DataType::Type::kInt32 ||
257 type == DataType::Type::kInt64);
258 DCHECK(type != DataType::Type::kInt16 || !reverseBits);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700259
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100260 if (type == DataType::Type::kInt16) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700261 Register in = locations->InAt(0).AsRegister<Register>();
262 Register out = locations->Out().AsRegister<Register>();
263
264 if (isR2OrNewer) {
265 __ Wsbh(out, in);
266 __ Seh(out, out);
267 } else {
268 __ Sll(TMP, in, 24);
269 __ Sra(TMP, TMP, 16);
270 __ Sll(out, in, 16);
271 __ Srl(out, out, 24);
272 __ Or(out, out, TMP);
273 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100274 } else if (type == DataType::Type::kInt32) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700275 Register in = locations->InAt(0).AsRegister<Register>();
276 Register out = locations->Out().AsRegister<Register>();
277
278 if (isR2OrNewer) {
279 __ Rotr(out, in, 16);
280 __ Wsbh(out, out);
281 } else {
282 // MIPS32r1
283 // __ Rotr(out, in, 16);
284 __ Sll(TMP, in, 16);
285 __ Srl(out, in, 16);
286 __ Or(out, out, TMP);
287 // __ Wsbh(out, out);
288 __ LoadConst32(AT, 0x00FF00FF);
289 __ And(TMP, out, AT);
290 __ Sll(TMP, TMP, 8);
291 __ Srl(out, out, 8);
292 __ And(out, out, AT);
293 __ Or(out, out, TMP);
294 }
Chris Larsen70014c82015-11-18 12:26:08 -0800295 if (reverseBits) {
296 if (isR6) {
297 __ Bitswap(out, out);
298 } else {
299 __ LoadConst32(AT, 0x0F0F0F0F);
300 __ And(TMP, out, AT);
301 __ Sll(TMP, TMP, 4);
302 __ Srl(out, out, 4);
303 __ And(out, out, AT);
304 __ Or(out, TMP, out);
305 __ LoadConst32(AT, 0x33333333);
306 __ And(TMP, out, AT);
307 __ Sll(TMP, TMP, 2);
308 __ Srl(out, out, 2);
309 __ And(out, out, AT);
310 __ Or(out, TMP, out);
311 __ LoadConst32(AT, 0x55555555);
312 __ And(TMP, out, AT);
313 __ Sll(TMP, TMP, 1);
314 __ Srl(out, out, 1);
315 __ And(out, out, AT);
316 __ Or(out, TMP, out);
317 }
318 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100319 } else if (type == DataType::Type::kInt64) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700320 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
321 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
322 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
323 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
324
325 if (isR2OrNewer) {
326 __ Rotr(AT, in_hi, 16);
327 __ Rotr(TMP, in_lo, 16);
328 __ Wsbh(out_lo, AT);
329 __ Wsbh(out_hi, TMP);
330 } else {
331 // When calling CreateIntToIntLocations() we promised that the
332 // use of the out_lo/out_hi wouldn't overlap with the use of
333 // in_lo/in_hi. Be very careful not to write to out_lo/out_hi
334 // until we're completely done reading from in_lo/in_hi.
335 // __ Rotr(TMP, in_lo, 16);
336 __ Sll(TMP, in_lo, 16);
337 __ Srl(AT, in_lo, 16);
338 __ Or(TMP, TMP, AT); // Hold in TMP until it's safe
339 // to write to out_hi.
340 // __ Rotr(out_lo, in_hi, 16);
341 __ Sll(AT, in_hi, 16);
342 __ Srl(out_lo, in_hi, 16); // Here we are finally done reading
343 // from in_lo/in_hi so it's okay to
344 // write to out_lo/out_hi.
345 __ Or(out_lo, out_lo, AT);
346 // __ Wsbh(out_hi, out_hi);
347 __ LoadConst32(AT, 0x00FF00FF);
348 __ And(out_hi, TMP, AT);
349 __ Sll(out_hi, out_hi, 8);
350 __ Srl(TMP, TMP, 8);
351 __ And(TMP, TMP, AT);
352 __ Or(out_hi, out_hi, TMP);
353 // __ Wsbh(out_lo, out_lo);
354 __ And(TMP, out_lo, AT); // AT already holds the correct mask value
355 __ Sll(TMP, TMP, 8);
356 __ Srl(out_lo, out_lo, 8);
357 __ And(out_lo, out_lo, AT);
358 __ Or(out_lo, out_lo, TMP);
359 }
Chris Larsen70014c82015-11-18 12:26:08 -0800360 if (reverseBits) {
361 if (isR6) {
362 __ Bitswap(out_hi, out_hi);
363 __ Bitswap(out_lo, out_lo);
364 } else {
365 __ LoadConst32(AT, 0x0F0F0F0F);
366 __ And(TMP, out_hi, AT);
367 __ Sll(TMP, TMP, 4);
368 __ Srl(out_hi, out_hi, 4);
369 __ And(out_hi, out_hi, AT);
370 __ Or(out_hi, TMP, out_hi);
371 __ And(TMP, out_lo, AT);
372 __ Sll(TMP, TMP, 4);
373 __ Srl(out_lo, out_lo, 4);
374 __ And(out_lo, out_lo, AT);
375 __ Or(out_lo, TMP, out_lo);
376 __ LoadConst32(AT, 0x33333333);
377 __ And(TMP, out_hi, AT);
378 __ Sll(TMP, TMP, 2);
379 __ Srl(out_hi, out_hi, 2);
380 __ And(out_hi, out_hi, AT);
381 __ Or(out_hi, TMP, out_hi);
382 __ And(TMP, out_lo, AT);
383 __ Sll(TMP, TMP, 2);
384 __ Srl(out_lo, out_lo, 2);
385 __ And(out_lo, out_lo, AT);
386 __ Or(out_lo, TMP, out_lo);
387 __ LoadConst32(AT, 0x55555555);
388 __ And(TMP, out_hi, AT);
389 __ Sll(TMP, TMP, 1);
390 __ Srl(out_hi, out_hi, 1);
391 __ And(out_hi, out_hi, AT);
392 __ Or(out_hi, TMP, out_hi);
393 __ And(TMP, out_lo, AT);
394 __ Sll(TMP, TMP, 1);
395 __ Srl(out_lo, out_lo, 1);
396 __ And(out_lo, out_lo, AT);
397 __ Or(out_lo, TMP, out_lo);
398 }
399 }
Chris Larsen3f8bf652015-10-28 10:08:56 -0700400 }
401}
402
403// int java.lang.Integer.reverseBytes(int)
404void IntrinsicLocationsBuilderMIPS::VisitIntegerReverseBytes(HInvoke* invoke) {
405 CreateIntToIntLocations(arena_, invoke);
406}
407
408void IntrinsicCodeGeneratorMIPS::VisitIntegerReverseBytes(HInvoke* invoke) {
Chris Larsen70014c82015-11-18 12:26:08 -0800409 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100410 DataType::Type::kInt32,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800411 IsR2OrNewer(),
412 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800413 /* reverseBits */ false,
Chris Larsen70014c82015-11-18 12:26:08 -0800414 GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700415}
416
417// long java.lang.Long.reverseBytes(long)
418void IntrinsicLocationsBuilderMIPS::VisitLongReverseBytes(HInvoke* invoke) {
419 CreateIntToIntLocations(arena_, invoke);
420}
421
422void IntrinsicCodeGeneratorMIPS::VisitLongReverseBytes(HInvoke* invoke) {
Chris Larsen70014c82015-11-18 12:26:08 -0800423 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100424 DataType::Type::kInt64,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800425 IsR2OrNewer(),
426 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800427 /* reverseBits */ false,
Chris Larsen70014c82015-11-18 12:26:08 -0800428 GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700429}
430
431// short java.lang.Short.reverseBytes(short)
432void IntrinsicLocationsBuilderMIPS::VisitShortReverseBytes(HInvoke* invoke) {
433 CreateIntToIntLocations(arena_, invoke);
434}
435
436void IntrinsicCodeGeneratorMIPS::VisitShortReverseBytes(HInvoke* invoke) {
Chris Larsen70014c82015-11-18 12:26:08 -0800437 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100438 DataType::Type::kInt16,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800439 IsR2OrNewer(),
440 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800441 /* reverseBits */ false,
Chris Larsen70014c82015-11-18 12:26:08 -0800442 GetAssembler());
443}
444
Chris Larsene3845472015-11-18 12:27:15 -0800445static void GenNumberOfLeadingZeroes(LocationSummary* locations,
446 bool is64bit,
447 bool isR6,
448 MipsAssembler* assembler) {
449 Register out = locations->Out().AsRegister<Register>();
450 if (is64bit) {
451 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
452 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
453
454 if (isR6) {
455 __ ClzR6(AT, in_hi);
456 __ ClzR6(TMP, in_lo);
457 __ Seleqz(TMP, TMP, in_hi);
458 } else {
459 __ ClzR2(AT, in_hi);
460 __ ClzR2(TMP, in_lo);
461 __ Movn(TMP, ZERO, in_hi);
462 }
463 __ Addu(out, AT, TMP);
464 } else {
465 Register in = locations->InAt(0).AsRegister<Register>();
466
467 if (isR6) {
468 __ ClzR6(out, in);
469 } else {
470 __ ClzR2(out, in);
471 }
472 }
473}
474
475// int java.lang.Integer.numberOfLeadingZeros(int i)
476void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
477 CreateIntToIntLocations(arena_, invoke);
478}
479
480void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800481 GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ false, IsR6(), GetAssembler());
Chris Larsene3845472015-11-18 12:27:15 -0800482}
483
484// int java.lang.Long.numberOfLeadingZeros(long i)
485void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
486 CreateIntToIntLocations(arena_, invoke);
487}
488
489void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800490 GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ true, IsR6(), GetAssembler());
Chris Larsene3845472015-11-18 12:27:15 -0800491}
492
Chris Larsen86829602015-11-18 12:27:52 -0800493static void GenNumberOfTrailingZeroes(LocationSummary* locations,
494 bool is64bit,
495 bool isR6,
Chris Larsen86829602015-11-18 12:27:52 -0800496 MipsAssembler* assembler) {
497 Register out = locations->Out().AsRegister<Register>();
498 Register in_lo;
499 Register in;
500
501 if (is64bit) {
Chris Larsen86829602015-11-18 12:27:52 -0800502 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
503
504 in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
505
506 // If in_lo is zero then count the number of trailing zeroes in in_hi;
507 // otherwise count the number of trailing zeroes in in_lo.
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800508 // out = in_lo ? in_lo : in_hi;
Chris Larsen86829602015-11-18 12:27:52 -0800509 if (isR6) {
510 __ Seleqz(out, in_hi, in_lo);
511 __ Selnez(TMP, in_lo, in_lo);
512 __ Or(out, out, TMP);
513 } else {
514 __ Movz(out, in_hi, in_lo);
515 __ Movn(out, in_lo, in_lo);
516 }
517
518 in = out;
519 } else {
520 in = locations->InAt(0).AsRegister<Register>();
521 // Give in_lo a dummy value to keep the compiler from complaining.
522 // Since we only get here in the 32-bit case, this value will never
523 // be used.
524 in_lo = in;
525 }
526
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800527 if (isR6) {
528 // We don't have an instruction to count the number of trailing zeroes.
529 // Start by flipping the bits end-for-end so we can count the number of
530 // leading zeroes instead.
Chris Larsen86829602015-11-18 12:27:52 -0800531 __ Rotr(out, in, 16);
532 __ Wsbh(out, out);
Chris Larsen86829602015-11-18 12:27:52 -0800533 __ Bitswap(out, out);
534 __ ClzR6(out, out);
535 } else {
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800536 // Convert trailing zeroes to trailing ones, and bits to their left
537 // to zeroes.
538 __ Addiu(TMP, in, -1);
539 __ Xor(out, TMP, in);
540 __ And(out, out, TMP);
541 // Count number of leading zeroes.
Chris Larsen86829602015-11-18 12:27:52 -0800542 __ ClzR2(out, out);
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800543 // Subtract number of leading zeroes from 32 to get number of trailing ones.
544 // Remember that the trailing ones were formerly trailing zeroes.
545 __ LoadConst32(TMP, 32);
546 __ Subu(out, TMP, out);
Chris Larsen86829602015-11-18 12:27:52 -0800547 }
548
549 if (is64bit) {
550 // If in_lo is zero, then we counted the number of trailing zeroes in in_hi so we must add the
551 // number of trailing zeroes in in_lo (32) to get the correct final count
552 __ LoadConst32(TMP, 32);
553 if (isR6) {
554 __ Seleqz(TMP, TMP, in_lo);
555 } else {
556 __ Movn(TMP, ZERO, in_lo);
557 }
558 __ Addu(out, out, TMP);
559 }
560}
561
562// int java.lang.Integer.numberOfTrailingZeros(int i)
563void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
564 CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap);
565}
566
567void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800568 GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ false, IsR6(), GetAssembler());
Chris Larsen86829602015-11-18 12:27:52 -0800569}
570
571// int java.lang.Long.numberOfTrailingZeros(long i)
572void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
573 CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap);
574}
575
576void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Chris Larsenbbb2ebe2016-02-17 17:44:58 -0800577 GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ true, IsR6(), GetAssembler());
Chris Larsene16ce5a2015-11-18 12:30:20 -0800578}
579
Chris Larsen70014c82015-11-18 12:26:08 -0800580// int java.lang.Integer.reverse(int)
581void IntrinsicLocationsBuilderMIPS::VisitIntegerReverse(HInvoke* invoke) {
582 CreateIntToIntLocations(arena_, invoke);
583}
584
585void IntrinsicCodeGeneratorMIPS::VisitIntegerReverse(HInvoke* invoke) {
586 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100587 DataType::Type::kInt32,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800588 IsR2OrNewer(),
589 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800590 /* reverseBits */ true,
Chris Larsen70014c82015-11-18 12:26:08 -0800591 GetAssembler());
592}
593
594// long java.lang.Long.reverse(long)
595void IntrinsicLocationsBuilderMIPS::VisitLongReverse(HInvoke* invoke) {
596 CreateIntToIntLocations(arena_, invoke);
597}
598
599void IntrinsicCodeGeneratorMIPS::VisitLongReverse(HInvoke* invoke) {
600 GenReverse(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100601 DataType::Type::kInt64,
Chris Larsene16ce5a2015-11-18 12:30:20 -0800602 IsR2OrNewer(),
603 IsR6(),
Chris Larsenb74353a2015-11-20 09:07:09 -0800604 /* reverseBits */ true,
Chris Larsen70014c82015-11-18 12:26:08 -0800605 GetAssembler());
Chris Larsen3f8bf652015-10-28 10:08:56 -0700606}
607
Chris Larsenb74353a2015-11-20 09:07:09 -0800608static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
609 LocationSummary* locations = new (arena) LocationSummary(invoke,
610 LocationSummary::kNoCall,
611 kIntrinsified);
612 locations->SetInAt(0, Location::RequiresFpuRegister());
613 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
614}
615
Chris Larsenedc16452016-02-12 17:59:00 -0800616static void GenBitCount(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100617 DataType::Type type,
Chris Larsenedc16452016-02-12 17:59:00 -0800618 bool isR6,
619 MipsAssembler* assembler) {
Chris Larsenedc16452016-02-12 17:59:00 -0800620 Register out = locations->Out().AsRegister<Register>();
621
622 // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
623 //
624 // A generalization of the best bit counting method to integers of
625 // bit-widths up to 128 (parameterized by type T) is this:
626 //
627 // v = v - ((v >> 1) & (T)~(T)0/3); // temp
628 // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
629 // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
630 // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; // count
631 //
632 // For comparison, for 32-bit quantities, this algorithm can be executed
633 // using 20 MIPS instructions (the calls to LoadConst32() generate two
634 // machine instructions each for the values being used in this algorithm).
635 // A(n unrolled) loop-based algorithm required 25 instructions.
636 //
637 // For 64-bit quantities, this algorithm gets executed twice, (once
638 // for in_lo, and again for in_hi), but saves a few instructions
639 // because the mask values only have to be loaded once. Using this
Chris Larsen8ca4f972016-04-14 16:16:29 -0700640 // algorithm the count for a 64-bit operand can be performed in 29
Chris Larsenedc16452016-02-12 17:59:00 -0800641 // instructions compared to a loop-based algorithm which required 47
642 // instructions.
643
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100644 if (type == DataType::Type::kInt32) {
Chris Larsenedc16452016-02-12 17:59:00 -0800645 Register in = locations->InAt(0).AsRegister<Register>();
646
647 __ Srl(TMP, in, 1);
648 __ LoadConst32(AT, 0x55555555);
649 __ And(TMP, TMP, AT);
650 __ Subu(TMP, in, TMP);
651 __ LoadConst32(AT, 0x33333333);
652 __ And(out, TMP, AT);
653 __ Srl(TMP, TMP, 2);
654 __ And(TMP, TMP, AT);
655 __ Addu(TMP, out, TMP);
656 __ Srl(out, TMP, 4);
657 __ Addu(out, out, TMP);
658 __ LoadConst32(AT, 0x0F0F0F0F);
659 __ And(out, out, AT);
660 __ LoadConst32(TMP, 0x01010101);
661 if (isR6) {
662 __ MulR6(out, out, TMP);
663 } else {
664 __ MulR2(out, out, TMP);
665 }
666 __ Srl(out, out, 24);
Roland Levillainfa3912e2016-04-01 18:21:55 +0100667 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100668 DCHECK_EQ(type, DataType::Type::kInt64);
Chris Larsenedc16452016-02-12 17:59:00 -0800669 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
670 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
671 Register tmp_hi = locations->GetTemp(0).AsRegister<Register>();
672 Register out_hi = locations->GetTemp(1).AsRegister<Register>();
673 Register tmp_lo = TMP;
674 Register out_lo = out;
675
676 __ Srl(tmp_lo, in_lo, 1);
677 __ Srl(tmp_hi, in_hi, 1);
678
679 __ LoadConst32(AT, 0x55555555);
680
681 __ And(tmp_lo, tmp_lo, AT);
682 __ Subu(tmp_lo, in_lo, tmp_lo);
683
684 __ And(tmp_hi, tmp_hi, AT);
685 __ Subu(tmp_hi, in_hi, tmp_hi);
686
687 __ LoadConst32(AT, 0x33333333);
688
689 __ And(out_lo, tmp_lo, AT);
690 __ Srl(tmp_lo, tmp_lo, 2);
691 __ And(tmp_lo, tmp_lo, AT);
692 __ Addu(tmp_lo, out_lo, tmp_lo);
Chris Larsenedc16452016-02-12 17:59:00 -0800693
694 __ And(out_hi, tmp_hi, AT);
695 __ Srl(tmp_hi, tmp_hi, 2);
696 __ And(tmp_hi, tmp_hi, AT);
697 __ Addu(tmp_hi, out_hi, tmp_hi);
Chris Larsenedc16452016-02-12 17:59:00 -0800698
Chris Larsen8ca4f972016-04-14 16:16:29 -0700699 // Here we deviate from the original algorithm a bit. We've reached
700 // the stage where the bitfields holding the subtotals are large
701 // enough to hold the combined subtotals for both the low word, and
702 // the high word. This means that we can add the subtotals for the
703 // the high, and low words into a single word, and compute the final
704 // result for both the high, and low words using fewer instructions.
Chris Larsenedc16452016-02-12 17:59:00 -0800705 __ LoadConst32(AT, 0x0F0F0F0F);
706
Chris Larsen8ca4f972016-04-14 16:16:29 -0700707 __ Addu(TMP, tmp_hi, tmp_lo);
708
709 __ Srl(out, TMP, 4);
710 __ And(out, out, AT);
711 __ And(TMP, TMP, AT);
712 __ Addu(out, out, TMP);
Chris Larsenedc16452016-02-12 17:59:00 -0800713
714 __ LoadConst32(AT, 0x01010101);
715
716 if (isR6) {
Chris Larsen8ca4f972016-04-14 16:16:29 -0700717 __ MulR6(out, out, AT);
Chris Larsenedc16452016-02-12 17:59:00 -0800718 } else {
Chris Larsen8ca4f972016-04-14 16:16:29 -0700719 __ MulR2(out, out, AT);
Chris Larsenedc16452016-02-12 17:59:00 -0800720 }
721
Chris Larsen8ca4f972016-04-14 16:16:29 -0700722 __ Srl(out, out, 24);
Chris Larsenedc16452016-02-12 17:59:00 -0800723 }
724}
725
726// int java.lang.Integer.bitCount(int)
727void IntrinsicLocationsBuilderMIPS::VisitIntegerBitCount(HInvoke* invoke) {
728 CreateIntToIntLocations(arena_, invoke);
729}
730
731void IntrinsicCodeGeneratorMIPS::VisitIntegerBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100732 GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), GetAssembler());
Chris Larsenedc16452016-02-12 17:59:00 -0800733}
734
735// int java.lang.Long.bitCount(int)
736void IntrinsicLocationsBuilderMIPS::VisitLongBitCount(HInvoke* invoke) {
737 LocationSummary* locations = new (arena_) LocationSummary(invoke,
738 LocationSummary::kNoCall,
739 kIntrinsified);
740 locations->SetInAt(0, Location::RequiresRegister());
741 locations->SetOut(Location::RequiresRegister());
742 locations->AddTemp(Location::RequiresRegister());
743 locations->AddTemp(Location::RequiresRegister());
744}
745
746void IntrinsicCodeGeneratorMIPS::VisitLongBitCount(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100747 GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler());
Chris Larsenedc16452016-02-12 17:59:00 -0800748}
749
Goran Jakovljevicb6684652017-01-11 13:42:38 +0100750static void MathAbsFP(LocationSummary* locations,
751 bool is64bit,
752 bool isR2OrNewer,
753 bool isR6,
754 MipsAssembler* assembler) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800755 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
756 FRegister out = locations->Out().AsFpuRegister<FRegister>();
757
Goran Jakovljevic5a6cbfc2017-01-13 12:13:39 +0100758 // Note, as a "quality of implementation", rather than pure "spec compliance", we require that
759 // Math.abs() clears the sign bit (but changes nothing else) for all numbers, including NaN
760 // (signaling NaN may become quiet though).
Goran Jakovljevicb6684652017-01-11 13:42:38 +0100761 //
762 // The ABS.fmt instructions (abs.s and abs.d) do exactly that when NAN2008=1 (R6). For this case,
763 // both regular floating point numbers and NAN values are treated alike, only the sign bit is
764 // affected by this instruction.
765 // But when NAN2008=0 (R2 and before), the ABS.fmt instructions can't be used. For this case, any
766 // NaN operand signals invalid operation. This means that other bits (not just sign bit) might be
767 // changed when doing abs(NaN). Because of that, we clear sign bit in a different way.
768 if (isR6) {
769 if (is64bit) {
770 __ AbsD(out, in);
771 } else {
772 __ AbsS(out, in);
773 }
Chris Larsenb74353a2015-11-20 09:07:09 -0800774 } else {
Goran Jakovljevicb6684652017-01-11 13:42:38 +0100775 if (is64bit) {
776 if (in != out) {
777 __ MovD(out, in);
778 }
779 __ MoveFromFpuHigh(TMP, in);
780 // ins instruction is not available for R1.
781 if (isR2OrNewer) {
782 __ Ins(TMP, ZERO, 31, 1);
783 } else {
784 __ Sll(TMP, TMP, 1);
785 __ Srl(TMP, TMP, 1);
786 }
787 __ MoveToFpuHigh(TMP, out);
788 } else {
789 __ Mfc1(TMP, in);
790 // ins instruction is not available for R1.
791 if (isR2OrNewer) {
792 __ Ins(TMP, ZERO, 31, 1);
793 } else {
794 __ Sll(TMP, TMP, 1);
795 __ Srl(TMP, TMP, 1);
796 }
797 __ Mtc1(TMP, out);
798 }
Chris Larsenb74353a2015-11-20 09:07:09 -0800799 }
800}
801
802// double java.lang.Math.abs(double)
803void IntrinsicLocationsBuilderMIPS::VisitMathAbsDouble(HInvoke* invoke) {
804 CreateFPToFPLocations(arena_, invoke);
805}
806
807void IntrinsicCodeGeneratorMIPS::VisitMathAbsDouble(HInvoke* invoke) {
Goran Jakovljevicb6684652017-01-11 13:42:38 +0100808 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, IsR2OrNewer(), IsR6(), GetAssembler());
Chris Larsenb74353a2015-11-20 09:07:09 -0800809}
810
811// float java.lang.Math.abs(float)
812void IntrinsicLocationsBuilderMIPS::VisitMathAbsFloat(HInvoke* invoke) {
813 CreateFPToFPLocations(arena_, invoke);
814}
815
816void IntrinsicCodeGeneratorMIPS::VisitMathAbsFloat(HInvoke* invoke) {
Goran Jakovljevicb6684652017-01-11 13:42:38 +0100817 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, IsR2OrNewer(), IsR6(), GetAssembler());
Chris Larsenb74353a2015-11-20 09:07:09 -0800818}
819
820static void GenAbsInteger(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
821 if (is64bit) {
822 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
823 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
824 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
825 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
826
827 // The comments in this section show the analogous operations which would
828 // be performed if we had 64-bit registers "in", and "out".
829 // __ Dsra32(AT, in, 31);
830 __ Sra(AT, in_hi, 31);
831 // __ Xor(out, in, AT);
832 __ Xor(TMP, in_lo, AT);
833 __ Xor(out_hi, in_hi, AT);
834 // __ Dsubu(out, out, AT);
835 __ Subu(out_lo, TMP, AT);
836 __ Sltu(TMP, out_lo, TMP);
837 __ Addu(out_hi, out_hi, TMP);
838 } else {
839 Register in = locations->InAt(0).AsRegister<Register>();
840 Register out = locations->Out().AsRegister<Register>();
841
842 __ Sra(AT, in, 31);
843 __ Xor(out, in, AT);
844 __ Subu(out, out, AT);
845 }
846}
847
848// int java.lang.Math.abs(int)
849void IntrinsicLocationsBuilderMIPS::VisitMathAbsInt(HInvoke* invoke) {
850 CreateIntToIntLocations(arena_, invoke);
851}
852
853void IntrinsicCodeGeneratorMIPS::VisitMathAbsInt(HInvoke* invoke) {
854 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
855}
856
857// long java.lang.Math.abs(long)
858void IntrinsicLocationsBuilderMIPS::VisitMathAbsLong(HInvoke* invoke) {
859 CreateIntToIntLocations(arena_, invoke);
860}
861
862void IntrinsicCodeGeneratorMIPS::VisitMathAbsLong(HInvoke* invoke) {
863 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
864}
865
866static void GenMinMaxFP(LocationSummary* locations,
867 bool is_min,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100868 DataType::Type type,
Chris Larsenb74353a2015-11-20 09:07:09 -0800869 bool is_R6,
870 MipsAssembler* assembler) {
871 FRegister out = locations->Out().AsFpuRegister<FRegister>();
872 FRegister a = locations->InAt(0).AsFpuRegister<FRegister>();
873 FRegister b = locations->InAt(1).AsFpuRegister<FRegister>();
874
875 if (is_R6) {
876 MipsLabel noNaNs;
877 MipsLabel done;
878 FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
879
880 // When Java computes min/max it prefers a NaN to a number; the
881 // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
882 // the inputs is a NaN and the other is a valid number, the MIPS
883 // instruction will return the number; Java wants the NaN value
884 // returned. This is why there is extra logic preceding the use of
885 // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
886 // NaN, return the NaN, otherwise return the min/max.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100887 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800888 __ CmpUnD(FTMP, a, b);
889 __ Bc1eqz(FTMP, &noNaNs);
890
891 // One of the inputs is a NaN
892 __ CmpEqD(ftmp, a, a);
893 // If a == a then b is the NaN, otherwise a is the NaN.
894 __ SelD(ftmp, a, b);
895
896 if (ftmp != out) {
897 __ MovD(out, ftmp);
898 }
899
900 __ B(&done);
901
902 __ Bind(&noNaNs);
903
904 if (is_min) {
905 __ MinD(out, a, b);
906 } else {
907 __ MaxD(out, a, b);
908 }
909 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100910 DCHECK_EQ(type, DataType::Type::kFloat32);
Chris Larsenb74353a2015-11-20 09:07:09 -0800911 __ CmpUnS(FTMP, a, b);
912 __ Bc1eqz(FTMP, &noNaNs);
913
914 // One of the inputs is a NaN
915 __ CmpEqS(ftmp, a, a);
916 // If a == a then b is the NaN, otherwise a is the NaN.
917 __ SelS(ftmp, a, b);
918
919 if (ftmp != out) {
920 __ MovS(out, ftmp);
921 }
922
923 __ B(&done);
924
925 __ Bind(&noNaNs);
926
927 if (is_min) {
928 __ MinS(out, a, b);
929 } else {
930 __ MaxS(out, a, b);
931 }
932 }
933
934 __ Bind(&done);
935 } else {
936 MipsLabel ordered;
937 MipsLabel compare;
938 MipsLabel select;
939 MipsLabel done;
940
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100941 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800942 __ CunD(a, b);
943 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100944 DCHECK_EQ(type, DataType::Type::kFloat32);
Chris Larsenb74353a2015-11-20 09:07:09 -0800945 __ CunS(a, b);
946 }
947 __ Bc1f(&ordered);
948
949 // a or b (or both) is a NaN. Return one, which is a NaN.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100950 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800951 __ CeqD(b, b);
952 } else {
953 __ CeqS(b, b);
954 }
955 __ B(&select);
956
957 __ Bind(&ordered);
958
959 // Neither is a NaN.
960 // a == b? (-0.0 compares equal with +0.0)
961 // If equal, handle zeroes, else compare further.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100962 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800963 __ CeqD(a, b);
964 } else {
965 __ CeqS(a, b);
966 }
967 __ Bc1f(&compare);
968
969 // a == b either bit for bit or one is -0.0 and the other is +0.0.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100970 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800971 __ MoveFromFpuHigh(TMP, a);
972 __ MoveFromFpuHigh(AT, b);
973 } else {
974 __ Mfc1(TMP, a);
975 __ Mfc1(AT, b);
976 }
977
978 if (is_min) {
979 // -0.0 prevails over +0.0.
980 __ Or(TMP, TMP, AT);
981 } else {
982 // +0.0 prevails over -0.0.
983 __ And(TMP, TMP, AT);
984 }
985
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100986 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800987 __ Mfc1(AT, a);
988 __ Mtc1(AT, out);
989 __ MoveToFpuHigh(TMP, out);
990 } else {
991 __ Mtc1(TMP, out);
992 }
993 __ B(&done);
994
995 __ Bind(&compare);
996
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100997 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -0800998 if (is_min) {
999 // return (a <= b) ? a : b;
1000 __ ColeD(a, b);
1001 } else {
1002 // return (a >= b) ? a : b;
1003 __ ColeD(b, a); // b <= a
1004 }
1005 } else {
1006 if (is_min) {
1007 // return (a <= b) ? a : b;
1008 __ ColeS(a, b);
1009 } else {
1010 // return (a >= b) ? a : b;
1011 __ ColeS(b, a); // b <= a
1012 }
1013 }
1014
1015 __ Bind(&select);
1016
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001017 if (type == DataType::Type::kFloat64) {
Chris Larsenb74353a2015-11-20 09:07:09 -08001018 __ MovtD(out, a);
1019 __ MovfD(out, b);
1020 } else {
1021 __ MovtS(out, a);
1022 __ MovfS(out, b);
1023 }
1024
1025 __ Bind(&done);
1026 }
1027}
1028
1029static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
1030 LocationSummary* locations = new (arena) LocationSummary(invoke,
1031 LocationSummary::kNoCall,
1032 kIntrinsified);
1033 locations->SetInAt(0, Location::RequiresFpuRegister());
1034 locations->SetInAt(1, Location::RequiresFpuRegister());
1035 locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap);
1036}
1037
1038// double java.lang.Math.min(double, double)
1039void IntrinsicLocationsBuilderMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) {
1040 CreateFPFPToFPLocations(arena_, invoke);
1041}
1042
1043void IntrinsicCodeGeneratorMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) {
1044 GenMinMaxFP(invoke->GetLocations(),
1045 /* is_min */ true,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001046 DataType::Type::kFloat64,
Chris Larsenb74353a2015-11-20 09:07:09 -08001047 IsR6(),
1048 GetAssembler());
1049}
1050
1051// float java.lang.Math.min(float, float)
1052void IntrinsicLocationsBuilderMIPS::VisitMathMinFloatFloat(HInvoke* invoke) {
1053 CreateFPFPToFPLocations(arena_, invoke);
1054}
1055
1056void IntrinsicCodeGeneratorMIPS::VisitMathMinFloatFloat(HInvoke* invoke) {
1057 GenMinMaxFP(invoke->GetLocations(),
1058 /* is_min */ true,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001059 DataType::Type::kFloat32,
Chris Larsenb74353a2015-11-20 09:07:09 -08001060 IsR6(),
1061 GetAssembler());
1062}
1063
1064// double java.lang.Math.max(double, double)
1065void IntrinsicLocationsBuilderMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) {
1066 CreateFPFPToFPLocations(arena_, invoke);
1067}
1068
1069void IntrinsicCodeGeneratorMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) {
1070 GenMinMaxFP(invoke->GetLocations(),
1071 /* is_min */ false,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001072 DataType::Type::kFloat64,
Chris Larsenb74353a2015-11-20 09:07:09 -08001073 IsR6(),
1074 GetAssembler());
1075}
1076
1077// float java.lang.Math.max(float, float)
1078void IntrinsicLocationsBuilderMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) {
1079 CreateFPFPToFPLocations(arena_, invoke);
1080}
1081
1082void IntrinsicCodeGeneratorMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) {
1083 GenMinMaxFP(invoke->GetLocations(),
1084 /* is_min */ false,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001085 DataType::Type::kFloat32,
Chris Larsenb74353a2015-11-20 09:07:09 -08001086 IsR6(),
1087 GetAssembler());
1088}
1089
1090static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
1091 LocationSummary* locations = new (arena) LocationSummary(invoke,
1092 LocationSummary::kNoCall,
1093 kIntrinsified);
1094 locations->SetInAt(0, Location::RequiresRegister());
1095 locations->SetInAt(1, Location::RequiresRegister());
1096 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1097}
1098
1099static void GenMinMax(LocationSummary* locations,
1100 bool is_min,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001101 DataType::Type type,
Chris Larsenb74353a2015-11-20 09:07:09 -08001102 bool is_R6,
1103 MipsAssembler* assembler) {
1104 if (is_R6) {
1105 // Some architectures, such as ARM and MIPS (prior to r6), have a
1106 // conditional move instruction which only changes the target
1107 // (output) register if the condition is true (MIPS prior to r6 had
1108 // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions
1109 // always change the target (output) register. If the condition is
1110 // true the output register gets the contents of the "rs" register;
1111 // otherwise, the output register is set to zero. One consequence
1112 // of this is that to implement something like "rd = c==0 ? rs : rt"
1113 // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions.
1114 // After executing this pair of instructions one of the output
1115 // registers from the pair will necessarily contain zero. Then the
1116 // code ORs the output registers from the SELEQZ/SELNEZ instructions
1117 // to get the final result.
1118 //
1119 // The initial test to see if the output register is same as the
1120 // first input register is needed to make sure that value in the
1121 // first input register isn't clobbered before we've finished
1122 // computing the output value. The logic in the corresponding else
1123 // clause performs the same task but makes sure the second input
1124 // register isn't clobbered in the event that it's the same register
1125 // as the output register; the else clause also handles the case
1126 // where the output register is distinct from both the first, and the
1127 // second input registers.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001128 if (type == DataType::Type::kInt64) {
Chris Larsenb74353a2015-11-20 09:07:09 -08001129 Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
1130 Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
1131 Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
1132 Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
1133 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
1134 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
1135
1136 MipsLabel compare_done;
1137
1138 if (a_lo == b_lo) {
1139 if (out_lo != a_lo) {
1140 __ Move(out_lo, a_lo);
1141 __ Move(out_hi, a_hi);
1142 }
1143 } else {
1144 __ Slt(TMP, b_hi, a_hi);
1145 __ Bne(b_hi, a_hi, &compare_done);
1146
1147 __ Sltu(TMP, b_lo, a_lo);
1148
1149 __ Bind(&compare_done);
1150
1151 if (is_min) {
1152 __ Seleqz(AT, a_lo, TMP);
1153 __ Selnez(out_lo, b_lo, TMP); // Safe even if out_lo == a_lo/b_lo
1154 // because at this point we're
1155 // done using a_lo/b_lo.
1156 } else {
1157 __ Selnez(AT, a_lo, TMP);
1158 __ Seleqz(out_lo, b_lo, TMP); // ditto
1159 }
1160 __ Or(out_lo, out_lo, AT);
1161 if (is_min) {
1162 __ Seleqz(AT, a_hi, TMP);
1163 __ Selnez(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi
1164 } else {
1165 __ Selnez(AT, a_hi, TMP);
1166 __ Seleqz(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi
1167 }
1168 __ Or(out_hi, out_hi, AT);
1169 }
1170 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001171 DCHECK_EQ(type, DataType::Type::kInt32);
Chris Larsenb74353a2015-11-20 09:07:09 -08001172 Register a = locations->InAt(0).AsRegister<Register>();
1173 Register b = locations->InAt(1).AsRegister<Register>();
1174 Register out = locations->Out().AsRegister<Register>();
1175
1176 if (a == b) {
1177 if (out != a) {
1178 __ Move(out, a);
1179 }
1180 } else {
1181 __ Slt(AT, b, a);
1182 if (is_min) {
1183 __ Seleqz(TMP, a, AT);
1184 __ Selnez(AT, b, AT);
1185 } else {
1186 __ Selnez(TMP, a, AT);
1187 __ Seleqz(AT, b, AT);
1188 }
1189 __ Or(out, TMP, AT);
1190 }
1191 }
1192 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001193 if (type == DataType::Type::kInt64) {
Chris Larsenb74353a2015-11-20 09:07:09 -08001194 Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
1195 Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
1196 Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
1197 Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
1198 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
1199 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
1200
1201 MipsLabel compare_done;
1202
1203 if (a_lo == b_lo) {
1204 if (out_lo != a_lo) {
1205 __ Move(out_lo, a_lo);
1206 __ Move(out_hi, a_hi);
1207 }
1208 } else {
1209 __ Slt(TMP, a_hi, b_hi);
1210 __ Bne(a_hi, b_hi, &compare_done);
1211
1212 __ Sltu(TMP, a_lo, b_lo);
1213
1214 __ Bind(&compare_done);
1215
1216 if (is_min) {
1217 if (out_lo != a_lo) {
1218 __ Movn(out_hi, a_hi, TMP);
1219 __ Movn(out_lo, a_lo, TMP);
1220 }
1221 if (out_lo != b_lo) {
1222 __ Movz(out_hi, b_hi, TMP);
1223 __ Movz(out_lo, b_lo, TMP);
1224 }
1225 } else {
1226 if (out_lo != a_lo) {
1227 __ Movz(out_hi, a_hi, TMP);
1228 __ Movz(out_lo, a_lo, TMP);
1229 }
1230 if (out_lo != b_lo) {
1231 __ Movn(out_hi, b_hi, TMP);
1232 __ Movn(out_lo, b_lo, TMP);
1233 }
1234 }
1235 }
1236 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001237 DCHECK_EQ(type, DataType::Type::kInt32);
Chris Larsenb74353a2015-11-20 09:07:09 -08001238 Register a = locations->InAt(0).AsRegister<Register>();
1239 Register b = locations->InAt(1).AsRegister<Register>();
1240 Register out = locations->Out().AsRegister<Register>();
1241
1242 if (a == b) {
1243 if (out != a) {
1244 __ Move(out, a);
1245 }
1246 } else {
1247 __ Slt(AT, a, b);
1248 if (is_min) {
1249 if (out != a) {
1250 __ Movn(out, a, AT);
1251 }
1252 if (out != b) {
1253 __ Movz(out, b, AT);
1254 }
1255 } else {
1256 if (out != a) {
1257 __ Movz(out, a, AT);
1258 }
1259 if (out != b) {
1260 __ Movn(out, b, AT);
1261 }
1262 }
1263 }
1264 }
1265 }
1266}
1267
1268// int java.lang.Math.min(int, int)
1269void IntrinsicLocationsBuilderMIPS::VisitMathMinIntInt(HInvoke* invoke) {
1270 CreateIntIntToIntLocations(arena_, invoke);
1271}
1272
1273void IntrinsicCodeGeneratorMIPS::VisitMathMinIntInt(HInvoke* invoke) {
1274 GenMinMax(invoke->GetLocations(),
1275 /* is_min */ true,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001276 DataType::Type::kInt32,
Chris Larsenb74353a2015-11-20 09:07:09 -08001277 IsR6(),
1278 GetAssembler());
1279}
1280
1281// long java.lang.Math.min(long, long)
1282void IntrinsicLocationsBuilderMIPS::VisitMathMinLongLong(HInvoke* invoke) {
1283 CreateIntIntToIntLocations(arena_, invoke);
1284}
1285
1286void IntrinsicCodeGeneratorMIPS::VisitMathMinLongLong(HInvoke* invoke) {
1287 GenMinMax(invoke->GetLocations(),
1288 /* is_min */ true,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001289 DataType::Type::kInt64,
Chris Larsenb74353a2015-11-20 09:07:09 -08001290 IsR6(),
1291 GetAssembler());
1292}
1293
1294// int java.lang.Math.max(int, int)
1295void IntrinsicLocationsBuilderMIPS::VisitMathMaxIntInt(HInvoke* invoke) {
1296 CreateIntIntToIntLocations(arena_, invoke);
1297}
1298
1299void IntrinsicCodeGeneratorMIPS::VisitMathMaxIntInt(HInvoke* invoke) {
1300 GenMinMax(invoke->GetLocations(),
1301 /* is_min */ false,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001302 DataType::Type::kInt32,
Chris Larsenb74353a2015-11-20 09:07:09 -08001303 IsR6(),
1304 GetAssembler());
1305}
1306
1307// long java.lang.Math.max(long, long)
1308void IntrinsicLocationsBuilderMIPS::VisitMathMaxLongLong(HInvoke* invoke) {
1309 CreateIntIntToIntLocations(arena_, invoke);
1310}
1311
1312void IntrinsicCodeGeneratorMIPS::VisitMathMaxLongLong(HInvoke* invoke) {
1313 GenMinMax(invoke->GetLocations(),
1314 /* is_min */ false,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001315 DataType::Type::kInt64,
Chris Larsenb74353a2015-11-20 09:07:09 -08001316 IsR6(),
1317 GetAssembler());
1318}
1319
1320// double java.lang.Math.sqrt(double)
1321void IntrinsicLocationsBuilderMIPS::VisitMathSqrt(HInvoke* invoke) {
1322 CreateFPToFPLocations(arena_, invoke);
1323}
1324
1325void IntrinsicCodeGeneratorMIPS::VisitMathSqrt(HInvoke* invoke) {
1326 LocationSummary* locations = invoke->GetLocations();
1327 MipsAssembler* assembler = GetAssembler();
1328 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
1329 FRegister out = locations->Out().AsFpuRegister<FRegister>();
1330
1331 __ SqrtD(out, in);
1332}
1333
Chris Larsen3acee732015-11-18 13:31:08 -08001334// byte libcore.io.Memory.peekByte(long address)
1335void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekByte(HInvoke* invoke) {
1336 CreateIntToIntLocations(arena_, invoke);
1337}
1338
1339void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekByte(HInvoke* invoke) {
1340 MipsAssembler* assembler = GetAssembler();
1341 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1342 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
1343
1344 __ Lb(out, adr, 0);
1345}
1346
1347// short libcore.io.Memory.peekShort(long address)
1348void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekShortNative(HInvoke* invoke) {
1349 CreateIntToIntLocations(arena_, invoke);
1350}
1351
1352void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekShortNative(HInvoke* invoke) {
1353 MipsAssembler* assembler = GetAssembler();
1354 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1355 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
1356
1357 if (IsR6()) {
1358 __ Lh(out, adr, 0);
1359 } else if (IsR2OrNewer()) {
1360 // Unlike for words, there are no lhl/lhr instructions to load
1361 // unaligned halfwords so the code loads individual bytes, in case
1362 // the address isn't halfword-aligned, and assembles them into a
1363 // signed halfword.
1364 __ Lb(AT, adr, 1); // This byte must be sign-extended.
1365 __ Lb(out, adr, 0); // This byte can be either sign-extended, or
1366 // zero-extended because the following
1367 // instruction overwrites the sign bits.
1368 __ Ins(out, AT, 8, 24);
1369 } else {
1370 __ Lbu(AT, adr, 0); // This byte must be zero-extended. If it's not
1371 // the "or" instruction below will destroy the upper
1372 // 24 bits of the final result.
1373 __ Lb(out, adr, 1); // This byte must be sign-extended.
1374 __ Sll(out, out, 8);
1375 __ Or(out, out, AT);
1376 }
1377}
1378
1379// int libcore.io.Memory.peekInt(long address)
1380void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekIntNative(HInvoke* invoke) {
1381 CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap);
1382}
1383
1384void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekIntNative(HInvoke* invoke) {
1385 MipsAssembler* assembler = GetAssembler();
1386 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1387 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
1388
1389 if (IsR6()) {
1390 __ Lw(out, adr, 0);
1391 } else {
1392 __ Lwr(out, adr, 0);
1393 __ Lwl(out, adr, 3);
1394 }
1395}
1396
1397// long libcore.io.Memory.peekLong(long address)
1398void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekLongNative(HInvoke* invoke) {
1399 CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap);
1400}
1401
1402void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekLongNative(HInvoke* invoke) {
1403 MipsAssembler* assembler = GetAssembler();
1404 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1405 Register out_lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
1406 Register out_hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
1407
1408 if (IsR6()) {
1409 __ Lw(out_lo, adr, 0);
1410 __ Lw(out_hi, adr, 4);
1411 } else {
1412 __ Lwr(out_lo, adr, 0);
1413 __ Lwl(out_lo, adr, 3);
1414 __ Lwr(out_hi, adr, 4);
1415 __ Lwl(out_hi, adr, 7);
1416 }
1417}
1418
1419static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
1420 LocationSummary* locations = new (arena) LocationSummary(invoke,
1421 LocationSummary::kNoCall,
1422 kIntrinsified);
1423 locations->SetInAt(0, Location::RequiresRegister());
1424 locations->SetInAt(1, Location::RequiresRegister());
1425}
1426
1427// void libcore.io.Memory.pokeByte(long address, byte value)
1428void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeByte(HInvoke* invoke) {
1429 CreateIntIntToVoidLocations(arena_, invoke);
1430}
1431
1432void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeByte(HInvoke* invoke) {
1433 MipsAssembler* assembler = GetAssembler();
1434 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1435 Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>();
1436
1437 __ Sb(val, adr, 0);
1438}
1439
1440// void libcore.io.Memory.pokeShort(long address, short value)
1441void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeShortNative(HInvoke* invoke) {
1442 CreateIntIntToVoidLocations(arena_, invoke);
1443}
1444
1445void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeShortNative(HInvoke* invoke) {
1446 MipsAssembler* assembler = GetAssembler();
1447 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1448 Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>();
1449
1450 if (IsR6()) {
1451 __ Sh(val, adr, 0);
1452 } else {
1453 // Unlike for words, there are no shl/shr instructions to store
1454 // unaligned halfwords so the code stores individual bytes, in case
1455 // the address isn't halfword-aligned.
1456 __ Sb(val, adr, 0);
1457 __ Srl(AT, val, 8);
1458 __ Sb(AT, adr, 1);
1459 }
1460}
1461
1462// void libcore.io.Memory.pokeInt(long address, int value)
1463void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeIntNative(HInvoke* invoke) {
1464 CreateIntIntToVoidLocations(arena_, invoke);
1465}
1466
1467void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeIntNative(HInvoke* invoke) {
1468 MipsAssembler* assembler = GetAssembler();
1469 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1470 Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>();
1471
1472 if (IsR6()) {
1473 __ Sw(val, adr, 0);
1474 } else {
1475 __ Swr(val, adr, 0);
1476 __ Swl(val, adr, 3);
1477 }
1478}
1479
1480// void libcore.io.Memory.pokeLong(long address, long value)
1481void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) {
1482 CreateIntIntToVoidLocations(arena_, invoke);
1483}
1484
1485void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) {
1486 MipsAssembler* assembler = GetAssembler();
1487 Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
1488 Register val_lo = invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>();
1489 Register val_hi = invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>();
1490
1491 if (IsR6()) {
1492 __ Sw(val_lo, adr, 0);
1493 __ Sw(val_hi, adr, 4);
1494 } else {
1495 __ Swr(val_lo, adr, 0);
1496 __ Swl(val_lo, adr, 3);
1497 __ Swr(val_hi, adr, 4);
1498 __ Swl(val_hi, adr, 7);
1499 }
1500}
1501
Chris Larsencf283da2016-01-19 16:45:35 -08001502// Thread java.lang.Thread.currentThread()
1503void IntrinsicLocationsBuilderMIPS::VisitThreadCurrentThread(HInvoke* invoke) {
1504 LocationSummary* locations = new (arena_) LocationSummary(invoke,
1505 LocationSummary::kNoCall,
1506 kIntrinsified);
1507 locations->SetOut(Location::RequiresRegister());
1508}
1509
1510void IntrinsicCodeGeneratorMIPS::VisitThreadCurrentThread(HInvoke* invoke) {
1511 MipsAssembler* assembler = GetAssembler();
1512 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
1513
1514 __ LoadFromOffset(kLoadWord,
1515 out,
1516 TR,
1517 Thread::PeerOffset<kMipsPointerSize>().Int32Value());
1518}
1519
Alexey Frunze15958152017-02-09 19:08:30 -08001520static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
1521 HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001522 DataType::Type type) {
Alexey Frunze15958152017-02-09 19:08:30 -08001523 bool can_call = kEmitCompilerReadBarrier &&
1524 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
1525 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001526 LocationSummary* locations = new (arena) LocationSummary(invoke,
Alexey Frunze15958152017-02-09 19:08:30 -08001527 (can_call
1528 ? LocationSummary::kCallOnSlowPath
1529 : LocationSummary::kNoCall),
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001530 kIntrinsified);
Alexey Frunzec61c0762017-04-10 13:54:23 -07001531 if (can_call && kUseBakerReadBarrier) {
1532 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
1533 }
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001534 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1535 locations->SetInAt(1, Location::RequiresRegister());
1536 locations->SetInAt(2, Location::RequiresRegister());
Alexey Frunze15958152017-02-09 19:08:30 -08001537 locations->SetOut(Location::RequiresRegister(),
1538 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001539 if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Alexey Frunze15958152017-02-09 19:08:30 -08001540 // We need a temporary register for the read barrier marking slow
1541 // path in InstructionCodeGeneratorMIPS::GenerateReferenceLoadWithBakerReadBarrier.
1542 locations->AddTemp(Location::RequiresRegister());
1543 }
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001544}
1545
Alexey Frunze15958152017-02-09 19:08:30 -08001546// Note that the caller must supply a properly aligned memory address.
1547// If they do not, the behavior is undefined (atomicity not guaranteed, exception may occur).
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001548static void GenUnsafeGet(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001549 DataType::Type type,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001550 bool is_volatile,
1551 bool is_R6,
1552 CodeGeneratorMIPS* codegen) {
1553 LocationSummary* locations = invoke->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001554 DCHECK((type == DataType::Type::kInt32) ||
1555 (type == DataType::Type::kInt64) ||
1556 (type == DataType::Type::kReference)) << type;
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001557 MipsAssembler* assembler = codegen->GetAssembler();
Alexey Frunze15958152017-02-09 19:08:30 -08001558 // Target register.
1559 Location trg_loc = locations->Out();
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001560 // Object pointer.
Alexey Frunze15958152017-02-09 19:08:30 -08001561 Location base_loc = locations->InAt(1);
1562 Register base = base_loc.AsRegister<Register>();
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001563 // The "offset" argument is passed as a "long". Since this code is for
1564 // a 32-bit processor, we can only use 32-bit addresses, so we only
1565 // need the low 32-bits of offset.
Alexey Frunze15958152017-02-09 19:08:30 -08001566 Location offset_loc = locations->InAt(2);
1567 Register offset_lo = offset_loc.AsRegisterPairLow<Register>();
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001568
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001569 if (!(kEmitCompilerReadBarrier && kUseBakerReadBarrier && (type == DataType::Type::kReference))) {
Alexey Frunze15958152017-02-09 19:08:30 -08001570 __ Addu(TMP, base, offset_lo);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001571 }
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001572
Alexey Frunze15958152017-02-09 19:08:30 -08001573 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001574 case DataType::Type::kInt64: {
Alexey Frunze15958152017-02-09 19:08:30 -08001575 Register trg_lo = trg_loc.AsRegisterPairLow<Register>();
1576 Register trg_hi = trg_loc.AsRegisterPairHigh<Register>();
1577 CHECK(!is_volatile); // TODO: support atomic 8-byte volatile loads.
1578 if (is_R6) {
1579 __ Lw(trg_lo, TMP, 0);
1580 __ Lw(trg_hi, TMP, 4);
1581 } else {
1582 __ Lwr(trg_lo, TMP, 0);
1583 __ Lwl(trg_lo, TMP, 3);
1584 __ Lwr(trg_hi, TMP, 4);
1585 __ Lwl(trg_hi, TMP, 7);
1586 }
1587 break;
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001588 }
Alexey Frunzec061de12017-02-14 13:27:23 -08001589
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001590 case DataType::Type::kInt32: {
Alexey Frunze15958152017-02-09 19:08:30 -08001591 Register trg = trg_loc.AsRegister<Register>();
1592 if (is_R6) {
1593 __ Lw(trg, TMP, 0);
1594 } else {
1595 __ Lwr(trg, TMP, 0);
1596 __ Lwl(trg, TMP, 3);
1597 }
1598 if (is_volatile) {
1599 __ Sync(0);
1600 }
1601 break;
Alexey Frunzec061de12017-02-14 13:27:23 -08001602 }
Alexey Frunze15958152017-02-09 19:08:30 -08001603
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001604 case DataType::Type::kReference: {
Alexey Frunze15958152017-02-09 19:08:30 -08001605 Register trg = trg_loc.AsRegister<Register>();
1606 if (kEmitCompilerReadBarrier) {
1607 if (kUseBakerReadBarrier) {
1608 Location temp = locations->GetTemp(0);
1609 codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
1610 trg_loc,
1611 base,
1612 /* offset */ 0U,
1613 /* index */ offset_loc,
1614 TIMES_1,
1615 temp,
1616 /* needs_null_check */ false);
1617 if (is_volatile) {
1618 __ Sync(0);
1619 }
1620 } else {
1621 if (is_R6) {
1622 __ Lw(trg, TMP, 0);
1623 } else {
1624 __ Lwr(trg, TMP, 0);
1625 __ Lwl(trg, TMP, 3);
1626 }
1627 if (is_volatile) {
1628 __ Sync(0);
1629 }
1630 codegen->GenerateReadBarrierSlow(invoke,
1631 trg_loc,
1632 trg_loc,
1633 base_loc,
1634 /* offset */ 0U,
1635 /* index */ offset_loc);
1636 }
1637 } else {
1638 if (is_R6) {
1639 __ Lw(trg, TMP, 0);
1640 } else {
1641 __ Lwr(trg, TMP, 0);
1642 __ Lwl(trg, TMP, 3);
1643 }
1644 if (is_volatile) {
1645 __ Sync(0);
1646 }
1647 __ MaybeUnpoisonHeapReference(trg);
1648 }
1649 break;
1650 }
1651
1652 default:
1653 LOG(FATAL) << "Unexpected type " << type;
1654 UNREACHABLE();
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001655 }
1656}
1657
1658// int sun.misc.Unsafe.getInt(Object o, long offset)
1659void IntrinsicLocationsBuilderMIPS::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001660 CreateIntIntIntToIntLocations(arena_, invoke, DataType::Type::kInt32);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001661}
1662
1663void IntrinsicCodeGeneratorMIPS::VisitUnsafeGet(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001664 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ false, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001665}
1666
1667// int sun.misc.Unsafe.getIntVolatile(Object o, long offset)
1668void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001669 CreateIntIntIntToIntLocations(arena_, invoke, DataType::Type::kInt32);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001670}
1671
1672void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001673 GenUnsafeGet(invoke, DataType::Type::kInt32, /* is_volatile */ true, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001674}
1675
1676// long sun.misc.Unsafe.getLong(Object o, long offset)
1677void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001678 CreateIntIntIntToIntLocations(arena_, invoke, DataType::Type::kInt64);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001679}
1680
1681void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetLong(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001682 GenUnsafeGet(invoke, DataType::Type::kInt64, /* is_volatile */ false, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001683}
1684
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001685// Object sun.misc.Unsafe.getObject(Object o, long offset)
1686void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001687 CreateIntIntIntToIntLocations(arena_, invoke, DataType::Type::kReference);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001688}
1689
1690void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObject(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001691 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ false, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001692}
1693
1694// Object sun.misc.Unsafe.getObjectVolatile(Object o, long offset)
1695void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001696 CreateIntIntIntToIntLocations(arena_, invoke, DataType::Type::kReference);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001697}
1698
1699void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001700 GenUnsafeGet(invoke, DataType::Type::kReference, /* is_volatile */ true, IsR6(), codegen_);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001701}
1702
1703static void CreateIntIntIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
1704 LocationSummary* locations = new (arena) LocationSummary(invoke,
1705 LocationSummary::kNoCall,
1706 kIntrinsified);
1707 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1708 locations->SetInAt(1, Location::RequiresRegister());
1709 locations->SetInAt(2, Location::RequiresRegister());
1710 locations->SetInAt(3, Location::RequiresRegister());
1711}
1712
Alexey Frunze15958152017-02-09 19:08:30 -08001713// Note that the caller must supply a properly aligned memory address.
1714// If they do not, the behavior is undefined (atomicity not guaranteed, exception may occur).
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001715static void GenUnsafePut(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001716 DataType::Type type,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001717 bool is_volatile,
1718 bool is_ordered,
1719 bool is_R6,
1720 CodeGeneratorMIPS* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001721 DCHECK((type == DataType::Type::kInt32) ||
1722 (type == DataType::Type::kInt64) ||
1723 (type == DataType::Type::kReference)) << type;
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001724 MipsAssembler* assembler = codegen->GetAssembler();
1725 // Object pointer.
1726 Register base = locations->InAt(1).AsRegister<Register>();
1727 // The "offset" argument is passed as a "long", i.e., it's 64-bits in
1728 // size. Since this code is for a 32-bit processor, we can only use
1729 // 32-bit addresses, so we only need the low 32-bits of offset.
1730 Register offset_lo = locations->InAt(2).AsRegisterPairLow<Register>();
1731
1732 __ Addu(TMP, base, offset_lo);
1733 if (is_volatile || is_ordered) {
1734 __ Sync(0);
1735 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001736 if ((type == DataType::Type::kInt32) || (type == DataType::Type::kReference)) {
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001737 Register value = locations->InAt(3).AsRegister<Register>();
1738
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001739 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Alexey Frunzec061de12017-02-14 13:27:23 -08001740 __ PoisonHeapReference(AT, value);
1741 value = AT;
1742 }
1743
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001744 if (is_R6) {
1745 __ Sw(value, TMP, 0);
1746 } else {
1747 __ Swr(value, TMP, 0);
1748 __ Swl(value, TMP, 3);
1749 }
1750 } else {
1751 Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
1752 Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
Alexey Frunze15958152017-02-09 19:08:30 -08001753 CHECK(!is_volatile); // TODO: support atomic 8-byte volatile stores.
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001754 if (is_R6) {
1755 __ Sw(value_lo, TMP, 0);
1756 __ Sw(value_hi, TMP, 4);
1757 } else {
1758 __ Swr(value_lo, TMP, 0);
1759 __ Swl(value_lo, TMP, 3);
1760 __ Swr(value_hi, TMP, 4);
1761 __ Swl(value_hi, TMP, 7);
1762 }
1763 }
1764
1765 if (is_volatile) {
1766 __ Sync(0);
1767 }
1768
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001769 if (type == DataType::Type::kReference) {
Goran Jakovljevice114da22016-12-26 14:21:43 +01001770 bool value_can_be_null = true; // TODO: Worth finding out this information?
1771 codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>(), value_can_be_null);
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001772 }
1773}
1774
1775// void sun.misc.Unsafe.putInt(Object o, long offset, int x)
1776void IntrinsicLocationsBuilderMIPS::VisitUnsafePut(HInvoke* invoke) {
1777 CreateIntIntIntIntToVoidLocations(arena_, invoke);
1778}
1779
1780void IntrinsicCodeGeneratorMIPS::VisitUnsafePut(HInvoke* invoke) {
1781 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001782 DataType::Type::kInt32,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001783 /* is_volatile */ false,
1784 /* is_ordered */ false,
1785 IsR6(),
1786 codegen_);
1787}
1788
1789// void sun.misc.Unsafe.putOrderedInt(Object o, long offset, int x)
1790void IntrinsicLocationsBuilderMIPS::VisitUnsafePutOrdered(HInvoke* invoke) {
1791 CreateIntIntIntIntToVoidLocations(arena_, invoke);
1792}
1793
1794void IntrinsicCodeGeneratorMIPS::VisitUnsafePutOrdered(HInvoke* invoke) {
1795 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001796 DataType::Type::kInt32,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001797 /* is_volatile */ false,
1798 /* is_ordered */ true,
1799 IsR6(),
1800 codegen_);
1801}
1802
1803// void sun.misc.Unsafe.putIntVolatile(Object o, long offset, int x)
1804void IntrinsicLocationsBuilderMIPS::VisitUnsafePutVolatile(HInvoke* invoke) {
1805 CreateIntIntIntIntToVoidLocations(arena_, invoke);
1806}
1807
1808void IntrinsicCodeGeneratorMIPS::VisitUnsafePutVolatile(HInvoke* invoke) {
1809 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001810 DataType::Type::kInt32,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001811 /* is_volatile */ true,
1812 /* is_ordered */ false,
1813 IsR6(),
1814 codegen_);
1815}
1816
1817// void sun.misc.Unsafe.putObject(Object o, long offset, Object x)
1818void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObject(HInvoke* invoke) {
1819 CreateIntIntIntIntToVoidLocations(arena_, invoke);
1820}
1821
1822void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObject(HInvoke* invoke) {
1823 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001824 DataType::Type::kReference,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001825 /* is_volatile */ false,
1826 /* is_ordered */ false,
1827 IsR6(),
1828 codegen_);
1829}
1830
1831// void sun.misc.Unsafe.putOrderedObject(Object o, long offset, Object x)
1832void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1833 CreateIntIntIntIntToVoidLocations(arena_, invoke);
1834}
1835
1836void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1837 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001838 DataType::Type::kReference,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001839 /* is_volatile */ false,
1840 /* is_ordered */ true,
1841 IsR6(),
1842 codegen_);
1843}
1844
1845// void sun.misc.Unsafe.putObjectVolatile(Object o, long offset, Object x)
1846void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1847 CreateIntIntIntIntToVoidLocations(arena_, invoke);
1848}
1849
1850void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1851 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001852 DataType::Type::kReference,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001853 /* is_volatile */ true,
1854 /* is_ordered */ false,
1855 IsR6(),
1856 codegen_);
1857}
1858
1859// void sun.misc.Unsafe.putLong(Object o, long offset, long x)
1860void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLong(HInvoke* invoke) {
1861 CreateIntIntIntIntToVoidLocations(arena_, invoke);
1862}
1863
1864void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLong(HInvoke* invoke) {
1865 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001866 DataType::Type::kInt64,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001867 /* is_volatile */ false,
1868 /* is_ordered */ false,
1869 IsR6(),
1870 codegen_);
1871}
1872
1873// void sun.misc.Unsafe.putOrderedLong(Object o, long offset, long x)
1874void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1875 CreateIntIntIntIntToVoidLocations(arena_, invoke);
1876}
1877
1878void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1879 GenUnsafePut(invoke->GetLocations(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001880 DataType::Type::kInt64,
Chris Larsen4fdc6d92015-12-14 13:26:14 -08001881 /* is_volatile */ false,
1882 /* is_ordered */ true,
1883 IsR6(),
1884 codegen_);
1885}
1886
Alexey Frunze15958152017-02-09 19:08:30 -08001887static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena, HInvoke* invoke) {
1888 bool can_call = kEmitCompilerReadBarrier &&
1889 kUseBakerReadBarrier &&
1890 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001891 LocationSummary* locations = new (arena) LocationSummary(invoke,
Alexey Frunze15958152017-02-09 19:08:30 -08001892 (can_call
1893 ? LocationSummary::kCallOnSlowPath
1894 : LocationSummary::kNoCall),
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001895 kIntrinsified);
1896 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1897 locations->SetInAt(1, Location::RequiresRegister());
1898 locations->SetInAt(2, Location::RequiresRegister());
1899 locations->SetInAt(3, Location::RequiresRegister());
1900 locations->SetInAt(4, Location::RequiresRegister());
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001901 locations->SetOut(Location::RequiresRegister());
Alexey Frunze15958152017-02-09 19:08:30 -08001902
1903 // Temporary register used in CAS by (Baker) read barrier.
1904 if (can_call) {
1905 locations->AddTemp(Location::RequiresRegister());
1906 }
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001907}
1908
Alexey Frunze15958152017-02-09 19:08:30 -08001909// Note that the caller must supply a properly aligned memory address.
1910// If they do not, the behavior is undefined (atomicity not guaranteed, exception may occur).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001911static void GenCas(HInvoke* invoke, DataType::Type type, CodeGeneratorMIPS* codegen) {
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001912 MipsAssembler* assembler = codegen->GetAssembler();
Alexey Frunze15958152017-02-09 19:08:30 -08001913 LocationSummary* locations = invoke->GetLocations();
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001914 bool isR6 = codegen->GetInstructionSetFeatures().IsR6();
1915 Register base = locations->InAt(1).AsRegister<Register>();
Alexey Frunze15958152017-02-09 19:08:30 -08001916 Location offset_loc = locations->InAt(2);
1917 Register offset_lo = offset_loc.AsRegisterPairLow<Register>();
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001918 Register expected = locations->InAt(3).AsRegister<Register>();
1919 Register value = locations->InAt(4).AsRegister<Register>();
Alexey Frunze15958152017-02-09 19:08:30 -08001920 Location out_loc = locations->Out();
1921 Register out = out_loc.AsRegister<Register>();
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001922
1923 DCHECK_NE(base, out);
1924 DCHECK_NE(offset_lo, out);
1925 DCHECK_NE(expected, out);
1926
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001927 if (type == DataType::Type::kReference) {
Alexey Frunze15958152017-02-09 19:08:30 -08001928 // The only read barrier implementation supporting the
1929 // UnsafeCASObject intrinsic is the Baker-style read barriers.
1930 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
1931
1932 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
1933 // object and scan the receiver at the next GC for nothing.
Goran Jakovljevice114da22016-12-26 14:21:43 +01001934 bool value_can_be_null = true; // TODO: Worth finding out this information?
1935 codegen->MarkGCCard(base, value, value_can_be_null);
Alexey Frunze15958152017-02-09 19:08:30 -08001936
1937 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
1938 Location temp = locations->GetTemp(0);
1939 // Need to make sure the reference stored in the field is a to-space
1940 // one before attempting the CAS or the CAS could fail incorrectly.
1941 codegen->GenerateReferenceLoadWithBakerReadBarrier(
1942 invoke,
1943 out_loc, // Unused, used only as a "temporary" within the read barrier.
1944 base,
1945 /* offset */ 0u,
1946 /* index */ offset_loc,
1947 ScaleFactor::TIMES_1,
1948 temp,
1949 /* needs_null_check */ false,
1950 /* always_update_field */ true);
1951 }
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001952 }
1953
Alexey Frunzec061de12017-02-14 13:27:23 -08001954 MipsLabel loop_head, exit_loop;
1955 __ Addu(TMP, base, offset_lo);
1956
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001957 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Alexey Frunzec061de12017-02-14 13:27:23 -08001958 __ PoisonHeapReference(expected);
1959 // Do not poison `value`, if it is the same register as
1960 // `expected`, which has just been poisoned.
1961 if (value != expected) {
1962 __ PoisonHeapReference(value);
1963 }
1964 }
1965
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001966 // do {
1967 // tmp_value = [tmp_ptr] - expected;
1968 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1969 // result = tmp_value != 0;
1970
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001971 __ Sync(0);
1972 __ Bind(&loop_head);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001973 if ((type == DataType::Type::kInt32) || (type == DataType::Type::kReference)) {
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001974 if (isR6) {
1975 __ LlR6(out, TMP);
1976 } else {
1977 __ LlR2(out, TMP);
1978 }
1979 } else {
Alexey Frunzec061de12017-02-14 13:27:23 -08001980 LOG(FATAL) << "Unsupported op size " << type;
1981 UNREACHABLE();
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001982 }
1983 __ Subu(out, out, expected); // If we didn't get the 'expected'
1984 __ Sltiu(out, out, 1); // value, set 'out' to false, and
1985 __ Beqz(out, &exit_loop); // return.
1986 __ Move(out, value); // Use 'out' for the 'store conditional' instruction.
1987 // If we use 'value' directly, we would lose 'value'
1988 // in the case that the store fails. Whether the
1989 // store succeeds, or fails, it will load the
Roland Levillain5e8d5f02016-10-18 18:03:43 +01001990 // correct Boolean value into the 'out' register.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001991 // This test isn't really necessary. We only support DataType::Type::kInt,
1992 // DataType::Type::kReference, and we already verified that we're working on one
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001993 // of those two types. It's left here in case the code needs to support
1994 // other types in the future.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001995 if ((type == DataType::Type::kInt32) || (type == DataType::Type::kReference)) {
Alexey Frunze51aff3a2016-03-17 17:21:45 -07001996 if (isR6) {
1997 __ ScR6(out, TMP);
1998 } else {
1999 __ ScR2(out, TMP);
2000 }
2001 }
2002 __ Beqz(out, &loop_head); // If we couldn't do the read-modify-write
2003 // cycle atomically then retry.
2004 __ Bind(&exit_loop);
2005 __ Sync(0);
Alexey Frunzec061de12017-02-14 13:27:23 -08002006
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002007 if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Alexey Frunzec061de12017-02-14 13:27:23 -08002008 __ UnpoisonHeapReference(expected);
2009 // Do not unpoison `value`, if it is the same register as
2010 // `expected`, which has just been unpoisoned.
2011 if (value != expected) {
2012 __ UnpoisonHeapReference(value);
2013 }
2014 }
Alexey Frunze51aff3a2016-03-17 17:21:45 -07002015}
2016
2017// boolean sun.misc.Unsafe.compareAndSwapInt(Object o, long offset, int expected, int x)
2018void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASInt(HInvoke* invoke) {
Alexey Frunze15958152017-02-09 19:08:30 -08002019 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07002020}
2021
2022void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASInt(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002023 GenCas(invoke, DataType::Type::kInt32, codegen_);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07002024}
2025
2026// boolean sun.misc.Unsafe.compareAndSwapObject(Object o, long offset, Object expected, Object x)
2027void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASObject(HInvoke* invoke) {
Alexey Frunze15958152017-02-09 19:08:30 -08002028 // The only read barrier implementation supporting the
2029 // UnsafeCASObject intrinsic is the Baker-style read barriers.
2030 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
2031 return;
2032 }
2033
2034 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07002035}
2036
2037void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASObject(HInvoke* invoke) {
Alexey Frunze15958152017-02-09 19:08:30 -08002038 // The only read barrier implementation supporting the
2039 // UnsafeCASObject intrinsic is the Baker-style read barriers.
2040 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
2041
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002042 GenCas(invoke, DataType::Type::kReference, codegen_);
Alexey Frunze51aff3a2016-03-17 17:21:45 -07002043}
2044
Chris Larsencf283da2016-01-19 16:45:35 -08002045// int java.lang.String.compareTo(String anotherString)
2046void IntrinsicLocationsBuilderMIPS::VisitStringCompareTo(HInvoke* invoke) {
2047 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescufca16662016-07-14 09:21:59 +01002048 LocationSummary::kCallOnMainAndSlowPath,
Chris Larsencf283da2016-01-19 16:45:35 -08002049 kIntrinsified);
2050 InvokeRuntimeCallingConvention calling_convention;
2051 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2052 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002053 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002054 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2055}
2056
2057void IntrinsicCodeGeneratorMIPS::VisitStringCompareTo(HInvoke* invoke) {
2058 MipsAssembler* assembler = GetAssembler();
2059 LocationSummary* locations = invoke->GetLocations();
2060
2061 // Note that the null check must have been done earlier.
2062 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
2063
2064 Register argument = locations->InAt(1).AsRegister<Register>();
2065 SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke);
2066 codegen_->AddSlowPath(slow_path);
2067 __ Beqz(argument, slow_path->GetEntryLabel());
Serban Constantinescufca16662016-07-14 09:21:59 +01002068 codegen_->InvokeRuntime(kQuickStringCompareTo, invoke, invoke->GetDexPc(), slow_path);
Chris Larsencf283da2016-01-19 16:45:35 -08002069 __ Bind(slow_path->GetExitLabel());
2070}
2071
Chris Larsen16ba2b42015-11-02 10:58:31 -08002072// boolean java.lang.String.equals(Object anObject)
2073void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) {
2074 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2075 LocationSummary::kNoCall,
2076 kIntrinsified);
2077 locations->SetInAt(0, Location::RequiresRegister());
2078 locations->SetInAt(1, Location::RequiresRegister());
2079 locations->SetOut(Location::RequiresRegister());
2080
2081 // Temporary registers to store lengths of strings and for calculations.
2082 locations->AddTemp(Location::RequiresRegister());
2083 locations->AddTemp(Location::RequiresRegister());
2084 locations->AddTemp(Location::RequiresRegister());
2085}
2086
2087void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) {
2088 MipsAssembler* assembler = GetAssembler();
2089 LocationSummary* locations = invoke->GetLocations();
2090
2091 Register str = locations->InAt(0).AsRegister<Register>();
2092 Register arg = locations->InAt(1).AsRegister<Register>();
2093 Register out = locations->Out().AsRegister<Register>();
2094
2095 Register temp1 = locations->GetTemp(0).AsRegister<Register>();
2096 Register temp2 = locations->GetTemp(1).AsRegister<Register>();
2097 Register temp3 = locations->GetTemp(2).AsRegister<Register>();
2098
2099 MipsLabel loop;
2100 MipsLabel end;
2101 MipsLabel return_true;
2102 MipsLabel return_false;
2103
2104 // Get offsets of count, value, and class fields within a string object.
2105 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2106 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
2107 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
2108
2109 // Note that the null check must have been done earlier.
2110 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
2111
2112 // If the register containing the pointer to "this", and the register
2113 // containing the pointer to "anObject" are the same register then
2114 // "this", and "anObject" are the same object and we can
2115 // short-circuit the logic to a true result.
2116 if (str == arg) {
2117 __ LoadConst32(out, 1);
2118 return;
2119 }
Goran Jakovljevic64fa84f2017-02-27 13:14:57 +01002120 StringEqualsOptimizations optimizations(invoke);
2121 if (!optimizations.GetArgumentNotNull()) {
2122 // Check if input is null, return false if it is.
2123 __ Beqz(arg, &return_false);
2124 }
Chris Larsen16ba2b42015-11-02 10:58:31 -08002125
2126 // Reference equality check, return true if same reference.
2127 __ Beq(str, arg, &return_true);
2128
Goran Jakovljevic64fa84f2017-02-27 13:14:57 +01002129 if (!optimizations.GetArgumentIsString()) {
2130 // Instanceof check for the argument by comparing class fields.
2131 // All string objects must have the same type since String cannot be subclassed.
2132 // Receiver must be a string object, so its class field is equal to all strings' class fields.
2133 // If the argument is a string object, its class field must be equal to receiver's class field.
2134 __ Lw(temp1, str, class_offset);
2135 __ Lw(temp2, arg, class_offset);
2136 __ Bne(temp1, temp2, &return_false);
2137 }
Chris Larsen16ba2b42015-11-02 10:58:31 -08002138
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002139 // Load `count` fields of this and argument strings.
Chris Larsen16ba2b42015-11-02 10:58:31 -08002140 __ Lw(temp1, str, count_offset);
2141 __ Lw(temp2, arg, count_offset);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002142 // Check if `count` fields are equal, return false if they're not.
2143 // Also compares the compression style, if differs return false.
Chris Larsen16ba2b42015-11-02 10:58:31 -08002144 __ Bne(temp1, temp2, &return_false);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002145 // Return true if both strings are empty. Even with string compression `count == 0` means empty.
2146 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
2147 "Expecting 0=compressed, 1=uncompressed");
Chris Larsen16ba2b42015-11-02 10:58:31 -08002148 __ Beqz(temp1, &return_true);
2149
2150 // Don't overwrite input registers
2151 __ Move(TMP, str);
2152 __ Move(temp3, arg);
2153
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002154 // Assertions that must hold in order to compare strings 4 bytes at a time.
Chris Larsen16ba2b42015-11-02 10:58:31 -08002155 DCHECK_ALIGNED(value_offset, 4);
2156 static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded");
2157
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002158 // For string compression, calculate the number of bytes to compare (not chars).
2159 if (mirror::kUseStringCompression) {
2160 // Extract compression flag.
2161 if (IsR2OrNewer()) {
2162 __ Ext(temp2, temp1, 0, 1);
2163 } else {
2164 __ Sll(temp2, temp1, 31);
2165 __ Srl(temp2, temp2, 31);
2166 }
2167 __ Srl(temp1, temp1, 1); // Extract length.
2168 __ Sllv(temp1, temp1, temp2); // Double the byte count if uncompressed.
2169 }
2170
2171 // Loop to compare strings 4 bytes at a time starting at the beginning of the string.
2172 // Ok to do this because strings are zero-padded to kObjectAlignment.
Chris Larsen16ba2b42015-11-02 10:58:31 -08002173 __ Bind(&loop);
2174 __ Lw(out, TMP, value_offset);
2175 __ Lw(temp2, temp3, value_offset);
2176 __ Bne(out, temp2, &return_false);
2177 __ Addiu(TMP, TMP, 4);
2178 __ Addiu(temp3, temp3, 4);
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002179 // With string compression, we have compared 4 bytes, otherwise 2 chars.
2180 __ Addiu(temp1, temp1, mirror::kUseStringCompression ? -4 : -2);
Chris Larsen16ba2b42015-11-02 10:58:31 -08002181 __ Bgtz(temp1, &loop);
2182
2183 // Return true and exit the function.
2184 // If loop does not result in returning false, we return true.
2185 __ Bind(&return_true);
2186 __ LoadConst32(out, 1);
2187 __ B(&end);
2188
2189 // Return false and exit the function.
2190 __ Bind(&return_false);
2191 __ LoadConst32(out, 0);
2192 __ Bind(&end);
2193}
2194
Chris Larsencf283da2016-01-19 16:45:35 -08002195static void GenerateStringIndexOf(HInvoke* invoke,
2196 bool start_at_zero,
2197 MipsAssembler* assembler,
2198 CodeGeneratorMIPS* codegen,
2199 ArenaAllocator* allocator) {
2200 LocationSummary* locations = invoke->GetLocations();
2201 Register tmp_reg = start_at_zero ? locations->GetTemp(0).AsRegister<Register>() : TMP;
2202
2203 // Note that the null check must have been done earlier.
2204 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
2205
Vladimir Markofb6c90a2016-05-06 15:52:12 +01002206 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
2207 // 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 -08002208 SlowPathCodeMIPS* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01002209 HInstruction* code_point = invoke->InputAt(1);
2210 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01002211 if (!IsUint<16>(code_point->AsIntConstant()->GetValue())) {
Chris Larsencf283da2016-01-19 16:45:35 -08002212 // Always needs the slow-path. We could directly dispatch to it,
2213 // but this case should be rare, so for simplicity just put the
2214 // full slow-path down and branch unconditionally.
2215 slow_path = new (allocator) IntrinsicSlowPathMIPS(invoke);
2216 codegen->AddSlowPath(slow_path);
2217 __ B(slow_path->GetEntryLabel());
2218 __ Bind(slow_path->GetExitLabel());
2219 return;
2220 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002221 } else if (code_point->GetType() != DataType::Type::kUint16) {
Chris Larsencf283da2016-01-19 16:45:35 -08002222 Register char_reg = locations->InAt(1).AsRegister<Register>();
2223 // The "bltu" conditional branch tests to see if the character value
2224 // fits in a valid 16-bit (MIPS halfword) value. If it doesn't then
2225 // the character being searched for, if it exists in the string, is
2226 // encoded using UTF-16 and stored in the string as two (16-bit)
2227 // halfwords. Currently the assembly code used to implement this
2228 // intrinsic doesn't support searching for a character stored as
2229 // two halfwords so we fallback to using the generic implementation
2230 // of indexOf().
2231 __ LoadConst32(tmp_reg, std::numeric_limits<uint16_t>::max());
2232 slow_path = new (allocator) IntrinsicSlowPathMIPS(invoke);
2233 codegen->AddSlowPath(slow_path);
2234 __ Bltu(tmp_reg, char_reg, slow_path->GetEntryLabel());
2235 }
2236
2237 if (start_at_zero) {
2238 DCHECK_EQ(tmp_reg, A2);
2239 // Start-index = 0.
2240 __ Clear(tmp_reg);
2241 }
2242
Serban Constantinescufca16662016-07-14 09:21:59 +01002243 codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
Chris Larsencf283da2016-01-19 16:45:35 -08002244 if (slow_path != nullptr) {
2245 __ Bind(slow_path->GetExitLabel());
2246 }
2247}
2248
2249// int java.lang.String.indexOf(int ch)
2250void IntrinsicLocationsBuilderMIPS::VisitStringIndexOf(HInvoke* invoke) {
2251 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00002252 LocationSummary::kCallOnMainAndSlowPath,
Chris Larsencf283da2016-01-19 16:45:35 -08002253 kIntrinsified);
2254 // We have a hand-crafted assembly stub that follows the runtime
2255 // calling convention. So it's best to align the inputs accordingly.
2256 InvokeRuntimeCallingConvention calling_convention;
2257 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2258 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002259 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002260 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2261
2262 // Need a temp for slow-path codepoint compare, and need to send start-index=0.
2263 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
2264}
2265
2266void IntrinsicCodeGeneratorMIPS::VisitStringIndexOf(HInvoke* invoke) {
2267 GenerateStringIndexOf(invoke,
2268 /* start_at_zero */ true,
2269 GetAssembler(),
2270 codegen_,
2271 GetAllocator());
2272}
2273
2274// int java.lang.String.indexOf(int ch, int fromIndex)
2275void IntrinsicLocationsBuilderMIPS::VisitStringIndexOfAfter(HInvoke* invoke) {
2276 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00002277 LocationSummary::kCallOnMainAndSlowPath,
Chris Larsencf283da2016-01-19 16:45:35 -08002278 kIntrinsified);
2279 // We have a hand-crafted assembly stub that follows the runtime
2280 // calling convention. So it's best to align the inputs accordingly.
2281 InvokeRuntimeCallingConvention calling_convention;
2282 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2283 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2284 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002285 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002286 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2287
2288 // Need a temp for slow-path codepoint compare.
2289 locations->AddTemp(Location::RequiresRegister());
2290}
2291
2292void IntrinsicCodeGeneratorMIPS::VisitStringIndexOfAfter(HInvoke* invoke) {
2293 GenerateStringIndexOf(invoke,
2294 /* start_at_zero */ false,
2295 GetAssembler(),
2296 codegen_,
2297 GetAllocator());
2298}
2299
2300// java.lang.StringFactory.newStringFromBytes(byte[] data, int high, int offset, int byteCount)
2301void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromBytes(HInvoke* invoke) {
2302 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00002303 LocationSummary::kCallOnMainAndSlowPath,
Chris Larsencf283da2016-01-19 16:45:35 -08002304 kIntrinsified);
2305 InvokeRuntimeCallingConvention calling_convention;
2306 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2307 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2308 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
2309 locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002310 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002311 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2312}
2313
2314void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromBytes(HInvoke* invoke) {
2315 MipsAssembler* assembler = GetAssembler();
2316 LocationSummary* locations = invoke->GetLocations();
2317
2318 Register byte_array = locations->InAt(0).AsRegister<Register>();
2319 SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke);
2320 codegen_->AddSlowPath(slow_path);
2321 __ Beqz(byte_array, slow_path->GetEntryLabel());
Serban Constantinescufca16662016-07-14 09:21:59 +01002322 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
Chris Larsencf283da2016-01-19 16:45:35 -08002323 __ Bind(slow_path->GetExitLabel());
2324}
2325
2326// java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
2327void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromChars(HInvoke* invoke) {
2328 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu54ff4822016-07-07 18:03:19 +01002329 LocationSummary::kCallOnMainOnly,
Chris Larsencf283da2016-01-19 16:45:35 -08002330 kIntrinsified);
2331 InvokeRuntimeCallingConvention calling_convention;
2332 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2333 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
2334 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002335 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002336 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2337}
2338
2339void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromChars(HInvoke* invoke) {
Chris Larsencf283da2016-01-19 16:45:35 -08002340 // No need to emit code checking whether `locations->InAt(2)` is a null
2341 // pointer, as callers of the native method
2342 //
2343 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
2344 //
2345 // all include a null check on `data` before calling that method.
Serban Constantinescufca16662016-07-14 09:21:59 +01002346 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Chris Larsencf283da2016-01-19 16:45:35 -08002347}
2348
2349// java.lang.StringFactory.newStringFromString(String toCopy)
2350void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromString(HInvoke* invoke) {
2351 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Serban Constantinescu806f0122016-03-09 11:10:16 +00002352 LocationSummary::kCallOnMainAndSlowPath,
Chris Larsencf283da2016-01-19 16:45:35 -08002353 kIntrinsified);
2354 InvokeRuntimeCallingConvention calling_convention;
2355 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002356 Location outLocation = calling_convention.GetReturnLocation(DataType::Type::kInt32);
Chris Larsencf283da2016-01-19 16:45:35 -08002357 locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<Register>()));
2358}
2359
2360void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromString(HInvoke* invoke) {
2361 MipsAssembler* assembler = GetAssembler();
2362 LocationSummary* locations = invoke->GetLocations();
2363
2364 Register string_to_copy = locations->InAt(0).AsRegister<Register>();
2365 SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke);
2366 codegen_->AddSlowPath(slow_path);
2367 __ Beqz(string_to_copy, slow_path->GetEntryLabel());
Serban Constantinescufca16662016-07-14 09:21:59 +01002368 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc());
Chris Larsencf283da2016-01-19 16:45:35 -08002369 __ Bind(slow_path->GetExitLabel());
2370}
2371
Chris Larsen2714fe62016-02-11 14:23:53 -08002372static void GenIsInfinite(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002373 const DataType::Type type,
Chris Larsen2714fe62016-02-11 14:23:53 -08002374 const bool isR6,
2375 MipsAssembler* assembler) {
2376 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
2377 Register out = locations->Out().AsRegister<Register>();
2378
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002379 DCHECK(type == DataType::Type::kFloat32 || type == DataType::Type::kFloat64);
Chris Larsen2714fe62016-02-11 14:23:53 -08002380
2381 if (isR6) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002382 if (type == DataType::Type::kFloat64) {
Chris Larsen2714fe62016-02-11 14:23:53 -08002383 __ ClassD(FTMP, in);
2384 } else {
2385 __ ClassS(FTMP, in);
2386 }
2387 __ Mfc1(out, FTMP);
2388 __ Andi(out, out, kPositiveInfinity | kNegativeInfinity);
2389 __ Sltu(out, ZERO, out);
2390 } else {
2391 // If one, or more, of the exponent bits is zero, then the number can't be infinite.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002392 if (type == DataType::Type::kFloat64) {
Chris Larsen2714fe62016-02-11 14:23:53 -08002393 __ MoveFromFpuHigh(TMP, in);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002394 __ LoadConst32(AT, High32Bits(kPositiveInfinityDouble));
Chris Larsen2714fe62016-02-11 14:23:53 -08002395 } else {
2396 __ Mfc1(TMP, in);
Anton Kirilova3ffea22016-04-07 17:02:37 +01002397 __ LoadConst32(AT, kPositiveInfinityFloat);
Chris Larsen2714fe62016-02-11 14:23:53 -08002398 }
2399 __ Xor(TMP, TMP, AT);
2400
2401 __ Sll(TMP, TMP, 1);
2402
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002403 if (type == DataType::Type::kFloat64) {
Chris Larsen2714fe62016-02-11 14:23:53 -08002404 __ Mfc1(AT, in);
2405 __ Or(TMP, TMP, AT);
2406 }
2407 // If any of the significand bits are one, then the number is not infinite.
2408 __ Sltiu(out, TMP, 1);
2409 }
2410}
2411
2412// boolean java.lang.Float.isInfinite(float)
2413void IntrinsicLocationsBuilderMIPS::VisitFloatIsInfinite(HInvoke* invoke) {
2414 CreateFPToIntLocations(arena_, invoke);
2415}
2416
2417void IntrinsicCodeGeneratorMIPS::VisitFloatIsInfinite(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002418 GenIsInfinite(invoke->GetLocations(), DataType::Type::kFloat32, IsR6(), GetAssembler());
Chris Larsen2714fe62016-02-11 14:23:53 -08002419}
2420
2421// boolean java.lang.Double.isInfinite(double)
2422void IntrinsicLocationsBuilderMIPS::VisitDoubleIsInfinite(HInvoke* invoke) {
2423 CreateFPToIntLocations(arena_, invoke);
2424}
2425
2426void IntrinsicCodeGeneratorMIPS::VisitDoubleIsInfinite(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002427 GenIsInfinite(invoke->GetLocations(), DataType::Type::kFloat64, IsR6(), GetAssembler());
Chris Larsen2714fe62016-02-11 14:23:53 -08002428}
2429
Chris Larsen97759342016-02-16 17:10:40 -08002430static void GenHighestOneBit(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002431 const DataType::Type type,
Chris Larsen97759342016-02-16 17:10:40 -08002432 bool isR6,
2433 MipsAssembler* assembler) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002434 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Chris Larsen97759342016-02-16 17:10:40 -08002435
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002436 if (type == DataType::Type::kInt64) {
Chris Larsen97759342016-02-16 17:10:40 -08002437 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2438 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2439 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
2440 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
2441
2442 if (isR6) {
2443 __ ClzR6(TMP, in_hi);
2444 } else {
2445 __ ClzR2(TMP, in_hi);
2446 }
2447 __ LoadConst32(AT, 0x80000000);
2448 __ Srlv(out_hi, AT, TMP);
2449 __ And(out_hi, out_hi, in_hi);
2450 if (isR6) {
2451 __ ClzR6(TMP, in_lo);
2452 } else {
2453 __ ClzR2(TMP, in_lo);
2454 }
2455 __ Srlv(out_lo, AT, TMP);
2456 __ And(out_lo, out_lo, in_lo);
2457 if (isR6) {
2458 __ Seleqz(out_lo, out_lo, out_hi);
2459 } else {
2460 __ Movn(out_lo, ZERO, out_hi);
2461 }
2462 } else {
2463 Register in = locations->InAt(0).AsRegister<Register>();
2464 Register out = locations->Out().AsRegister<Register>();
2465
2466 if (isR6) {
2467 __ ClzR6(TMP, in);
2468 } else {
2469 __ ClzR2(TMP, in);
2470 }
2471 __ LoadConst32(AT, 0x80000000);
2472 __ Srlv(AT, AT, TMP); // Srlv shifts in the range of [0;31] bits (lower 5 bits of arg).
2473 __ And(out, AT, in); // So this is required for 0 (=shift by 32).
2474 }
2475}
2476
2477// int java.lang.Integer.highestOneBit(int)
2478void IntrinsicLocationsBuilderMIPS::VisitIntegerHighestOneBit(HInvoke* invoke) {
2479 CreateIntToIntLocations(arena_, invoke);
2480}
2481
2482void IntrinsicCodeGeneratorMIPS::VisitIntegerHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002483 GenHighestOneBit(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), GetAssembler());
Chris Larsen97759342016-02-16 17:10:40 -08002484}
2485
2486// long java.lang.Long.highestOneBit(long)
2487void IntrinsicLocationsBuilderMIPS::VisitLongHighestOneBit(HInvoke* invoke) {
2488 CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap);
2489}
2490
2491void IntrinsicCodeGeneratorMIPS::VisitLongHighestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002492 GenHighestOneBit(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler());
Chris Larsen97759342016-02-16 17:10:40 -08002493}
2494
2495static void GenLowestOneBit(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002496 const DataType::Type type,
Chris Larsen97759342016-02-16 17:10:40 -08002497 bool isR6,
2498 MipsAssembler* assembler) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002499 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Chris Larsen97759342016-02-16 17:10:40 -08002500
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002501 if (type == DataType::Type::kInt64) {
Chris Larsen97759342016-02-16 17:10:40 -08002502 Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
2503 Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
2504 Register out_lo = locations->Out().AsRegisterPairLow<Register>();
2505 Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
2506
2507 __ Subu(TMP, ZERO, in_lo);
2508 __ And(out_lo, TMP, in_lo);
2509 __ Subu(TMP, ZERO, in_hi);
2510 __ And(out_hi, TMP, in_hi);
2511 if (isR6) {
2512 __ Seleqz(out_hi, out_hi, out_lo);
2513 } else {
2514 __ Movn(out_hi, ZERO, out_lo);
2515 }
2516 } else {
2517 Register in = locations->InAt(0).AsRegister<Register>();
2518 Register out = locations->Out().AsRegister<Register>();
2519
2520 __ Subu(TMP, ZERO, in);
2521 __ And(out, TMP, in);
2522 }
2523}
2524
2525// int java.lang.Integer.lowestOneBit(int)
2526void IntrinsicLocationsBuilderMIPS::VisitIntegerLowestOneBit(HInvoke* invoke) {
2527 CreateIntToIntLocations(arena_, invoke);
2528}
2529
2530void IntrinsicCodeGeneratorMIPS::VisitIntegerLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002531 GenLowestOneBit(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), GetAssembler());
Chris Larsen97759342016-02-16 17:10:40 -08002532}
2533
2534// long java.lang.Long.lowestOneBit(long)
2535void IntrinsicLocationsBuilderMIPS::VisitLongLowestOneBit(HInvoke* invoke) {
2536 CreateIntToIntLocations(arena_, invoke);
2537}
2538
2539void IntrinsicCodeGeneratorMIPS::VisitLongLowestOneBit(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002540 GenLowestOneBit(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler());
Chris Larsen97759342016-02-16 17:10:40 -08002541}
2542
Chris Larsenf09d5322016-04-22 12:06:34 -07002543// int java.lang.Math.round(float)
2544void IntrinsicLocationsBuilderMIPS::VisitMathRoundFloat(HInvoke* invoke) {
2545 LocationSummary* locations = new (arena_) LocationSummary(invoke,
2546 LocationSummary::kNoCall,
2547 kIntrinsified);
2548 locations->SetInAt(0, Location::RequiresFpuRegister());
2549 locations->AddTemp(Location::RequiresFpuRegister());
2550 locations->SetOut(Location::RequiresRegister());
2551}
2552
2553void IntrinsicCodeGeneratorMIPS::VisitMathRoundFloat(HInvoke* invoke) {
2554 LocationSummary* locations = invoke->GetLocations();
2555 MipsAssembler* assembler = GetAssembler();
2556 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
2557 FRegister half = locations->GetTemp(0).AsFpuRegister<FRegister>();
2558 Register out = locations->Out().AsRegister<Register>();
2559
2560 MipsLabel done;
Chris Larsenf09d5322016-04-22 12:06:34 -07002561
Chris Larsenf09d5322016-04-22 12:06:34 -07002562 if (IsR6()) {
Lena Djokicf4e23a82017-05-09 15:43:45 +02002563 // out = floor(in);
2564 //
2565 // if (out != MAX_VALUE && out != MIN_VALUE) {
2566 // TMP = ((in - out) >= 0.5) ? 1 : 0;
2567 // return out += TMP;
2568 // }
2569 // return out;
Chris Larsenf09d5322016-04-22 12:06:34 -07002570
Lena Djokicf4e23a82017-05-09 15:43:45 +02002571 // out = floor(in);
2572 __ FloorWS(FTMP, in);
2573 __ Mfc1(out, FTMP);
Chris Larsenf09d5322016-04-22 12:06:34 -07002574
Lena Djokicf4e23a82017-05-09 15:43:45 +02002575 // if (out != MAX_VALUE && out != MIN_VALUE)
2576 __ Addiu(TMP, out, 1);
2577 __ Aui(TMP, TMP, 0x8000); // TMP = out + 0x8000 0001
2578 // or out - 0x7FFF FFFF.
2579 // IOW, TMP = 1 if out = Int.MIN_VALUE
2580 // or TMP = 0 if out = Int.MAX_VALUE.
2581 __ Srl(TMP, TMP, 1); // TMP = 0 if out = Int.MIN_VALUE
2582 // or out = Int.MAX_VALUE.
2583 __ Beqz(TMP, &done);
Chris Larsenf09d5322016-04-22 12:06:34 -07002584
Lena Djokicf4e23a82017-05-09 15:43:45 +02002585 // TMP = (0.5f <= (in - out)) ? -1 : 0;
2586 __ Cvtsw(FTMP, FTMP); // Convert output of floor.w.s back to "float".
2587 __ LoadConst32(AT, bit_cast<int32_t, float>(0.5f));
2588 __ SubS(FTMP, in, FTMP);
2589 __ Mtc1(AT, half);
Chris Larsenf09d5322016-04-22 12:06:34 -07002590
Chris Larsenf09d5322016-04-22 12:06:34 -07002591 __ CmpLeS(FTMP, half, FTMP);
Chris Larsen07f712f2016-06-10 16:06:02 -07002592 __ Mfc1(TMP, FTMP);
Lena Djokicf4e23a82017-05-09 15:43:45 +02002593
2594 // Return out -= TMP.
2595 __ Subu(out, out, TMP);
Chris Larsenf09d5322016-04-22 12:06:34 -07002596 } else {
Lena Djokicf4e23a82017-05-09 15:43:45 +02002597 // if (in.isNaN) {
2598 // return 0;
2599 // }
2600 //
2601 // out = floor.w.s(in);
2602 //
2603 // /*
2604 // * This "if" statement is only needed for the pre-R6 version of floor.w.s
2605 // * which outputs Integer.MAX_VALUE for negative numbers with magnitudes
2606 // * too large to fit in a 32-bit integer.
2607 // */
2608 // if (out == Integer.MAX_VALUE) {
2609 // TMP = (in < 0.0f) ? 1 : 0;
2610 // /*
2611 // * If TMP is 1, then adding it to out will wrap its value from
2612 // * Integer.MAX_VALUE to Integer.MIN_VALUE.
2613 // */
2614 // return out += TMP;
2615 // }
2616 //
2617 // /*
2618 // * For negative values not handled by the previous "if" statement the
2619 // * test here will correctly set the value of TMP.
2620 // */
2621 // TMP = ((in - out) >= 0.5f) ? 1 : 0;
2622 // return out += TMP;
2623
2624 MipsLabel finite;
2625 MipsLabel add;
2626
2627 // Test for NaN.
2628 __ CunS(in, in);
2629
2630 // Return zero for NaN.
2631 __ Move(out, ZERO);
2632 __ Bc1t(&done);
2633
2634 // out = floor(in);
2635 __ FloorWS(FTMP, in);
2636 __ Mfc1(out, FTMP);
2637
2638 __ LoadConst32(TMP, -1);
2639
2640 // TMP = (out = java.lang.Integer.MAX_VALUE) ? -1 : 0;
2641 __ LoadConst32(AT, std::numeric_limits<int32_t>::max());
2642 __ Bne(AT, out, &finite);
2643
2644 __ Mtc1(ZERO, FTMP);
2645 __ ColtS(in, FTMP);
2646
2647 __ B(&add);
2648
2649 __ Bind(&finite);
2650
2651 // TMP = (0.5f <= (in - out)) ? -1 : 0;
2652 __ Cvtsw(FTMP, FTMP); // Convert output of floor.w.s back to "float".
2653 __ LoadConst32(AT, bit_cast<int32_t, float>(0.5f));
2654 __ SubS(FTMP, in, FTMP);
2655 __ Mtc1(AT, half);
Chris Larsenf09d5322016-04-22 12:06:34 -07002656 __ ColeS(half, FTMP);
Chris Larsenf09d5322016-04-22 12:06:34 -07002657
Lena Djokicf4e23a82017-05-09 15:43:45 +02002658 __ Bind(&add);
Chris Larsenf09d5322016-04-22 12:06:34 -07002659
Chris Larsenf09d5322016-04-22 12:06:34 -07002660 __ Movf(TMP, ZERO);
Lena Djokicf4e23a82017-05-09 15:43:45 +02002661
2662 // Return out -= TMP.
2663 __ Subu(out, out, TMP);
Chris Larsenf09d5322016-04-22 12:06:34 -07002664 }
Chris Larsenf09d5322016-04-22 12:06:34 -07002665 __ Bind(&done);
2666}
2667
Chris Larsen692235e2016-11-21 16:04:53 -08002668// void java.lang.String.getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
2669void IntrinsicLocationsBuilderMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2670 LocationSummary* locations = new (arena_) LocationSummary(invoke,
Chris Larsenfe4ff442017-03-23 11:25:12 -07002671 LocationSummary::kNoCall,
Chris Larsen692235e2016-11-21 16:04:53 -08002672 kIntrinsified);
2673 locations->SetInAt(0, Location::RequiresRegister());
2674 locations->SetInAt(1, Location::RequiresRegister());
2675 locations->SetInAt(2, Location::RequiresRegister());
2676 locations->SetInAt(3, Location::RequiresRegister());
2677 locations->SetInAt(4, Location::RequiresRegister());
2678
Chris Larsenfe4ff442017-03-23 11:25:12 -07002679 locations->AddTemp(Location::RequiresRegister());
2680 locations->AddTemp(Location::RequiresRegister());
2681 locations->AddTemp(Location::RequiresRegister());
Chris Larsen692235e2016-11-21 16:04:53 -08002682}
2683
2684void IntrinsicCodeGeneratorMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
2685 MipsAssembler* assembler = GetAssembler();
2686 LocationSummary* locations = invoke->GetLocations();
2687
2688 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002689 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Chris Larsen692235e2016-11-21 16:04:53 -08002690 DCHECK_EQ(char_size, 2u);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002691 const size_t char_shift = DataType::SizeShift(DataType::Type::kUint16);
Chris Larsen692235e2016-11-21 16:04:53 -08002692
2693 Register srcObj = locations->InAt(0).AsRegister<Register>();
2694 Register srcBegin = locations->InAt(1).AsRegister<Register>();
2695 Register srcEnd = locations->InAt(2).AsRegister<Register>();
2696 Register dstObj = locations->InAt(3).AsRegister<Register>();
2697 Register dstBegin = locations->InAt(4).AsRegister<Register>();
2698
2699 Register dstPtr = locations->GetTemp(0).AsRegister<Register>();
Chris Larsen692235e2016-11-21 16:04:53 -08002700 Register srcPtr = locations->GetTemp(1).AsRegister<Register>();
Chris Larsen692235e2016-11-21 16:04:53 -08002701 Register numChrs = locations->GetTemp(2).AsRegister<Register>();
Chris Larsen692235e2016-11-21 16:04:53 -08002702
2703 MipsLabel done;
Chris Larsenfe4ff442017-03-23 11:25:12 -07002704 MipsLabel loop;
Chris Larsen692235e2016-11-21 16:04:53 -08002705
2706 // Location of data in char array buffer.
2707 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
2708
2709 // Get offset of value field within a string object.
2710 const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
2711
2712 __ Beq(srcEnd, srcBegin, &done); // No characters to move.
2713
2714 // Calculate number of characters to be copied.
2715 __ Subu(numChrs, srcEnd, srcBegin);
2716
2717 // Calculate destination address.
2718 __ Addiu(dstPtr, dstObj, data_offset);
Chris Larsencd0295d2017-03-31 15:26:54 -07002719 __ ShiftAndAdd(dstPtr, dstBegin, dstPtr, char_shift);
Chris Larsen692235e2016-11-21 16:04:53 -08002720
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002721 if (mirror::kUseStringCompression) {
2722 MipsLabel uncompressed_copy, compressed_loop;
2723 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2724 // Load count field and extract compression flag.
2725 __ LoadFromOffset(kLoadWord, TMP, srcObj, count_offset);
2726 __ Sll(TMP, TMP, 31);
2727
Chris Larsenfe4ff442017-03-23 11:25:12 -07002728 // If string is uncompressed, use uncompressed path.
Goran Jakovljevicf94fa812017-02-10 17:48:52 +01002729 __ Bnez(TMP, &uncompressed_copy);
2730
2731 // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
2732 __ Addu(srcPtr, srcObj, srcBegin);
2733 __ Bind(&compressed_loop);
2734 __ LoadFromOffset(kLoadUnsignedByte, TMP, srcPtr, value_offset);
2735 __ StoreToOffset(kStoreHalfword, TMP, dstPtr, 0);
2736 __ Addiu(numChrs, numChrs, -1);
2737 __ Addiu(srcPtr, srcPtr, 1);
2738 __ Addiu(dstPtr, dstPtr, 2);
2739 __ Bnez(numChrs, &compressed_loop);
2740
2741 __ B(&done);
2742 __ Bind(&uncompressed_copy);
2743 }
2744
Chris Larsen692235e2016-11-21 16:04:53 -08002745 // Calculate source address.
2746 __ Addiu(srcPtr, srcObj, value_offset);
Chris Larsencd0295d2017-03-31 15:26:54 -07002747 __ ShiftAndAdd(srcPtr, srcBegin, srcPtr, char_shift);
Chris Larsen692235e2016-11-21 16:04:53 -08002748
Chris Larsenfe4ff442017-03-23 11:25:12 -07002749 __ Bind(&loop);
2750 __ Lh(AT, srcPtr, 0);
2751 __ Addiu(numChrs, numChrs, -1);
2752 __ Addiu(srcPtr, srcPtr, char_size);
2753 __ Sh(AT, dstPtr, 0);
2754 __ Addiu(dstPtr, dstPtr, char_size);
2755 __ Bnez(numChrs, &loop);
Chris Larsen692235e2016-11-21 16:04:53 -08002756
2757 __ Bind(&done);
2758}
2759
Chris Larsenb9005fa2017-03-24 12:11:54 -07002760static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2761 LocationSummary* locations = new (arena) LocationSummary(invoke,
2762 LocationSummary::kCallOnMainOnly,
2763 kIntrinsified);
2764 InvokeRuntimeCallingConvention calling_convention;
2765
2766 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002767 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kFloat64));
Chris Larsenb9005fa2017-03-24 12:11:54 -07002768}
2769
2770static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
2771 LocationSummary* locations = new (arena) LocationSummary(invoke,
2772 LocationSummary::kCallOnMainOnly,
2773 kIntrinsified);
2774 InvokeRuntimeCallingConvention calling_convention;
2775
2776 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
2777 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002778 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kFloat64));
Chris Larsenb9005fa2017-03-24 12:11:54 -07002779}
2780
2781static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorMIPS* codegen, QuickEntrypointEnum entry) {
2782 LocationSummary* locations = invoke->GetLocations();
2783 FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
2784 DCHECK_EQ(in, F12);
2785 FRegister out = locations->Out().AsFpuRegister<FRegister>();
2786 DCHECK_EQ(out, F0);
2787
2788 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2789}
2790
2791static void GenFPFPToFPCall(HInvoke* invoke,
2792 CodeGeneratorMIPS* codegen,
2793 QuickEntrypointEnum entry) {
2794 LocationSummary* locations = invoke->GetLocations();
2795 FRegister in0 = locations->InAt(0).AsFpuRegister<FRegister>();
2796 DCHECK_EQ(in0, F12);
2797 FRegister in1 = locations->InAt(1).AsFpuRegister<FRegister>();
2798 DCHECK_EQ(in1, F14);
2799 FRegister out = locations->Out().AsFpuRegister<FRegister>();
2800 DCHECK_EQ(out, F0);
2801
2802 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
2803}
2804
2805// static double java.lang.Math.cos(double a)
2806void IntrinsicLocationsBuilderMIPS::VisitMathCos(HInvoke* invoke) {
2807 CreateFPToFPCallLocations(arena_, invoke);
2808}
2809
2810void IntrinsicCodeGeneratorMIPS::VisitMathCos(HInvoke* invoke) {
2811 GenFPToFPCall(invoke, codegen_, kQuickCos);
2812}
2813
2814// static double java.lang.Math.sin(double a)
2815void IntrinsicLocationsBuilderMIPS::VisitMathSin(HInvoke* invoke) {
2816 CreateFPToFPCallLocations(arena_, invoke);
2817}
2818
2819void IntrinsicCodeGeneratorMIPS::VisitMathSin(HInvoke* invoke) {
2820 GenFPToFPCall(invoke, codegen_, kQuickSin);
2821}
2822
2823// static double java.lang.Math.acos(double a)
2824void IntrinsicLocationsBuilderMIPS::VisitMathAcos(HInvoke* invoke) {
2825 CreateFPToFPCallLocations(arena_, invoke);
2826}
2827
2828void IntrinsicCodeGeneratorMIPS::VisitMathAcos(HInvoke* invoke) {
2829 GenFPToFPCall(invoke, codegen_, kQuickAcos);
2830}
2831
2832// static double java.lang.Math.asin(double a)
2833void IntrinsicLocationsBuilderMIPS::VisitMathAsin(HInvoke* invoke) {
2834 CreateFPToFPCallLocations(arena_, invoke);
2835}
2836
2837void IntrinsicCodeGeneratorMIPS::VisitMathAsin(HInvoke* invoke) {
2838 GenFPToFPCall(invoke, codegen_, kQuickAsin);
2839}
2840
2841// static double java.lang.Math.atan(double a)
2842void IntrinsicLocationsBuilderMIPS::VisitMathAtan(HInvoke* invoke) {
2843 CreateFPToFPCallLocations(arena_, invoke);
2844}
2845
2846void IntrinsicCodeGeneratorMIPS::VisitMathAtan(HInvoke* invoke) {
2847 GenFPToFPCall(invoke, codegen_, kQuickAtan);
2848}
2849
2850// static double java.lang.Math.atan2(double y, double x)
2851void IntrinsicLocationsBuilderMIPS::VisitMathAtan2(HInvoke* invoke) {
2852 CreateFPFPToFPCallLocations(arena_, invoke);
2853}
2854
2855void IntrinsicCodeGeneratorMIPS::VisitMathAtan2(HInvoke* invoke) {
2856 GenFPFPToFPCall(invoke, codegen_, kQuickAtan2);
2857}
2858
2859// static double java.lang.Math.cbrt(double a)
2860void IntrinsicLocationsBuilderMIPS::VisitMathCbrt(HInvoke* invoke) {
2861 CreateFPToFPCallLocations(arena_, invoke);
2862}
2863
2864void IntrinsicCodeGeneratorMIPS::VisitMathCbrt(HInvoke* invoke) {
2865 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
2866}
2867
2868// static double java.lang.Math.cosh(double x)
2869void IntrinsicLocationsBuilderMIPS::VisitMathCosh(HInvoke* invoke) {
2870 CreateFPToFPCallLocations(arena_, invoke);
2871}
2872
2873void IntrinsicCodeGeneratorMIPS::VisitMathCosh(HInvoke* invoke) {
2874 GenFPToFPCall(invoke, codegen_, kQuickCosh);
2875}
2876
2877// static double java.lang.Math.exp(double a)
2878void IntrinsicLocationsBuilderMIPS::VisitMathExp(HInvoke* invoke) {
2879 CreateFPToFPCallLocations(arena_, invoke);
2880}
2881
2882void IntrinsicCodeGeneratorMIPS::VisitMathExp(HInvoke* invoke) {
2883 GenFPToFPCall(invoke, codegen_, kQuickExp);
2884}
2885
2886// static double java.lang.Math.expm1(double x)
2887void IntrinsicLocationsBuilderMIPS::VisitMathExpm1(HInvoke* invoke) {
2888 CreateFPToFPCallLocations(arena_, invoke);
2889}
2890
2891void IntrinsicCodeGeneratorMIPS::VisitMathExpm1(HInvoke* invoke) {
2892 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
2893}
2894
2895// static double java.lang.Math.hypot(double x, double y)
2896void IntrinsicLocationsBuilderMIPS::VisitMathHypot(HInvoke* invoke) {
2897 CreateFPFPToFPCallLocations(arena_, invoke);
2898}
2899
2900void IntrinsicCodeGeneratorMIPS::VisitMathHypot(HInvoke* invoke) {
2901 GenFPFPToFPCall(invoke, codegen_, kQuickHypot);
2902}
2903
2904// static double java.lang.Math.log(double a)
2905void IntrinsicLocationsBuilderMIPS::VisitMathLog(HInvoke* invoke) {
2906 CreateFPToFPCallLocations(arena_, invoke);
2907}
2908
2909void IntrinsicCodeGeneratorMIPS::VisitMathLog(HInvoke* invoke) {
2910 GenFPToFPCall(invoke, codegen_, kQuickLog);
2911}
2912
2913// static double java.lang.Math.log10(double x)
2914void IntrinsicLocationsBuilderMIPS::VisitMathLog10(HInvoke* invoke) {
2915 CreateFPToFPCallLocations(arena_, invoke);
2916}
2917
2918void IntrinsicCodeGeneratorMIPS::VisitMathLog10(HInvoke* invoke) {
2919 GenFPToFPCall(invoke, codegen_, kQuickLog10);
2920}
2921
2922// static double java.lang.Math.nextAfter(double start, double direction)
2923void IntrinsicLocationsBuilderMIPS::VisitMathNextAfter(HInvoke* invoke) {
2924 CreateFPFPToFPCallLocations(arena_, invoke);
2925}
2926
2927void IntrinsicCodeGeneratorMIPS::VisitMathNextAfter(HInvoke* invoke) {
2928 GenFPFPToFPCall(invoke, codegen_, kQuickNextAfter);
2929}
2930
2931// static double java.lang.Math.sinh(double x)
2932void IntrinsicLocationsBuilderMIPS::VisitMathSinh(HInvoke* invoke) {
2933 CreateFPToFPCallLocations(arena_, invoke);
2934}
2935
2936void IntrinsicCodeGeneratorMIPS::VisitMathSinh(HInvoke* invoke) {
2937 GenFPToFPCall(invoke, codegen_, kQuickSinh);
2938}
2939
2940// static double java.lang.Math.tan(double a)
2941void IntrinsicLocationsBuilderMIPS::VisitMathTan(HInvoke* invoke) {
2942 CreateFPToFPCallLocations(arena_, invoke);
2943}
2944
2945void IntrinsicCodeGeneratorMIPS::VisitMathTan(HInvoke* invoke) {
2946 GenFPToFPCall(invoke, codegen_, kQuickTan);
2947}
2948
2949// static double java.lang.Math.tanh(double x)
2950void IntrinsicLocationsBuilderMIPS::VisitMathTanh(HInvoke* invoke) {
2951 CreateFPToFPCallLocations(arena_, invoke);
2952}
2953
2954void IntrinsicCodeGeneratorMIPS::VisitMathTanh(HInvoke* invoke) {
2955 GenFPToFPCall(invoke, codegen_, kQuickTanh);
2956}
2957
Chris Larsen2f6ad9d2017-03-23 15:37:03 -07002958// static void java.lang.System.arraycopy(Object src, int srcPos,
2959// Object dest, int destPos,
2960// int length)
2961void IntrinsicLocationsBuilderMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) {
2962 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
2963 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
2964 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
2965
2966 // As long as we are checking, we might as well check to see if the src and dest
2967 // positions are >= 0.
2968 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
2969 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
2970 // We will have to fail anyways.
2971 return;
2972 }
2973
2974 // And since we are already checking, check the length too.
2975 if (length != nullptr) {
2976 int32_t len = length->GetValue();
2977 if (len < 0) {
2978 // Just call as normal.
2979 return;
2980 }
2981 }
2982
2983 // Okay, it is safe to generate inline code.
2984 LocationSummary* locations =
2985 new (arena_) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
2986 // arraycopy(Object src, int srcPos, Object dest, int destPos, int length).
2987 locations->SetInAt(0, Location::RequiresRegister());
2988 locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
2989 locations->SetInAt(2, Location::RequiresRegister());
2990 locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3)));
2991 locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4)));
2992
2993 locations->AddTemp(Location::RequiresRegister());
2994 locations->AddTemp(Location::RequiresRegister());
2995 locations->AddTemp(Location::RequiresRegister());
2996}
2997
2998// Utility routine to verify that "length(input) - pos >= length"
2999static void EnoughItems(MipsAssembler* assembler,
3000 Register length_input_minus_pos,
3001 Location length,
3002 SlowPathCodeMIPS* slow_path) {
3003 if (length.IsConstant()) {
3004 int32_t length_constant = length.GetConstant()->AsIntConstant()->GetValue();
3005
3006 if (IsInt<16>(length_constant)) {
3007 __ Slti(TMP, length_input_minus_pos, length_constant);
3008 __ Bnez(TMP, slow_path->GetEntryLabel());
3009 } else {
3010 __ LoadConst32(TMP, length_constant);
3011 __ Blt(length_input_minus_pos, TMP, slow_path->GetEntryLabel());
3012 }
3013 } else {
3014 __ Blt(length_input_minus_pos, length.AsRegister<Register>(), slow_path->GetEntryLabel());
3015 }
3016}
3017
3018static void CheckPosition(MipsAssembler* assembler,
3019 Location pos,
3020 Register input,
3021 Location length,
3022 SlowPathCodeMIPS* slow_path,
3023 bool length_is_input_length = false) {
3024 // Where is the length in the Array?
3025 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
3026
3027 // Calculate length(input) - pos.
3028 if (pos.IsConstant()) {
3029 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
3030 if (pos_const == 0) {
3031 if (!length_is_input_length) {
3032 // Check that length(input) >= length.
3033 __ LoadFromOffset(kLoadWord, AT, input, length_offset);
3034 EnoughItems(assembler, AT, length, slow_path);
3035 }
3036 } else {
3037 // Check that (length(input) - pos) >= zero.
3038 __ LoadFromOffset(kLoadWord, AT, input, length_offset);
3039 DCHECK_GT(pos_const, 0);
3040 __ Addiu32(AT, AT, -pos_const, TMP);
3041 __ Bltz(AT, slow_path->GetEntryLabel());
3042
3043 // Verify that (length(input) - pos) >= length.
3044 EnoughItems(assembler, AT, length, slow_path);
3045 }
3046 } else if (length_is_input_length) {
3047 // The only way the copy can succeed is if pos is zero.
3048 Register pos_reg = pos.AsRegister<Register>();
3049 __ Bnez(pos_reg, slow_path->GetEntryLabel());
3050 } else {
3051 // Verify that pos >= 0.
3052 Register pos_reg = pos.AsRegister<Register>();
3053 __ Bltz(pos_reg, slow_path->GetEntryLabel());
3054
3055 // Check that (length(input) - pos) >= zero.
3056 __ LoadFromOffset(kLoadWord, AT, input, length_offset);
3057 __ Subu(AT, AT, pos_reg);
3058 __ Bltz(AT, slow_path->GetEntryLabel());
3059
3060 // Verify that (length(input) - pos) >= length.
3061 EnoughItems(assembler, AT, length, slow_path);
3062 }
3063}
3064
3065void IntrinsicCodeGeneratorMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) {
3066 MipsAssembler* assembler = GetAssembler();
3067 LocationSummary* locations = invoke->GetLocations();
3068
3069 Register src = locations->InAt(0).AsRegister<Register>();
3070 Location src_pos = locations->InAt(1);
3071 Register dest = locations->InAt(2).AsRegister<Register>();
3072 Location dest_pos = locations->InAt(3);
3073 Location length = locations->InAt(4);
3074
3075 MipsLabel loop;
3076
3077 Register dest_base = locations->GetTemp(0).AsRegister<Register>();
3078 Register src_base = locations->GetTemp(1).AsRegister<Register>();
3079 Register count = locations->GetTemp(2).AsRegister<Register>();
3080
3081 SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke);
3082 codegen_->AddSlowPath(slow_path);
3083
3084 // Bail out if the source and destination are the same (to handle overlap).
3085 __ Beq(src, dest, slow_path->GetEntryLabel());
3086
3087 // Bail out if the source is null.
3088 __ Beqz(src, slow_path->GetEntryLabel());
3089
3090 // Bail out if the destination is null.
3091 __ Beqz(dest, slow_path->GetEntryLabel());
3092
3093 // Load length into register for count.
3094 if (length.IsConstant()) {
3095 __ LoadConst32(count, length.GetConstant()->AsIntConstant()->GetValue());
3096 } else {
3097 // If the length is negative, bail out.
3098 // We have already checked in the LocationsBuilder for the constant case.
3099 __ Bltz(length.AsRegister<Register>(), slow_path->GetEntryLabel());
3100
3101 __ Move(count, length.AsRegister<Register>());
3102 }
3103
3104 // Validity checks: source.
3105 CheckPosition(assembler, src_pos, src, Location::RegisterLocation(count), slow_path);
3106
3107 // Validity checks: dest.
3108 CheckPosition(assembler, dest_pos, dest, Location::RegisterLocation(count), slow_path);
3109
3110 // If count is zero, we're done.
3111 __ Beqz(count, slow_path->GetExitLabel());
3112
3113 // Okay, everything checks out. Finally time to do the copy.
3114 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003115 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Chris Larsen2f6ad9d2017-03-23 15:37:03 -07003116 DCHECK_EQ(char_size, 2u);
3117
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003118 const size_t char_shift = DataType::SizeShift(DataType::Type::kUint16);
Chris Larsen2f6ad9d2017-03-23 15:37:03 -07003119
3120 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
3121
3122 // Calculate source and destination addresses.
3123 if (src_pos.IsConstant()) {
3124 int32_t src_pos_const = src_pos.GetConstant()->AsIntConstant()->GetValue();
3125
3126 __ Addiu32(src_base, src, data_offset + char_size * src_pos_const, TMP);
3127 } else {
3128 __ Addiu32(src_base, src, data_offset, TMP);
3129 __ ShiftAndAdd(src_base, src_pos.AsRegister<Register>(), src_base, char_shift);
3130 }
3131 if (dest_pos.IsConstant()) {
3132 int32_t dest_pos_const = dest_pos.GetConstant()->AsIntConstant()->GetValue();
3133
3134 __ Addiu32(dest_base, dest, data_offset + char_size * dest_pos_const, TMP);
3135 } else {
3136 __ Addiu32(dest_base, dest, data_offset, TMP);
3137 __ ShiftAndAdd(dest_base, dest_pos.AsRegister<Register>(), dest_base, char_shift);
3138 }
3139
3140 __ Bind(&loop);
3141 __ Lh(TMP, src_base, 0);
3142 __ Addiu(src_base, src_base, char_size);
3143 __ Addiu(count, count, -1);
3144 __ Sh(TMP, dest_base, 0);
3145 __ Addiu(dest_base, dest_base, char_size);
3146 __ Bnez(count, &loop);
3147
3148 __ Bind(slow_path->GetExitLabel());
3149}
3150
Chris Larsen5633ce72017-04-10 15:47:40 -07003151// long java.lang.Integer.valueOf(long)
3152void IntrinsicLocationsBuilderMIPS::VisitIntegerValueOf(HInvoke* invoke) {
3153 InvokeRuntimeCallingConvention calling_convention;
3154 IntrinsicVisitor::ComputeIntegerValueOfLocations(
3155 invoke,
3156 codegen_,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003157 calling_convention.GetReturnLocation(DataType::Type::kReference),
Chris Larsen5633ce72017-04-10 15:47:40 -07003158 Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
3159}
3160
3161void IntrinsicCodeGeneratorMIPS::VisitIntegerValueOf(HInvoke* invoke) {
3162 IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
3163 LocationSummary* locations = invoke->GetLocations();
3164 MipsAssembler* assembler = GetAssembler();
3165 InstructionCodeGeneratorMIPS* icodegen =
3166 down_cast<InstructionCodeGeneratorMIPS*>(codegen_->GetInstructionVisitor());
3167
3168 Register out = locations->Out().AsRegister<Register>();
3169 InvokeRuntimeCallingConvention calling_convention;
3170 if (invoke->InputAt(0)->IsConstant()) {
3171 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
3172 if (value >= info.low && value <= info.high) {
3173 // Just embed the j.l.Integer in the code.
3174 ScopedObjectAccess soa(Thread::Current());
3175 mirror::Object* boxed = info.cache->Get(value + (-info.low));
3176 DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
3177 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
3178 __ LoadConst32(out, address);
3179 } else {
3180 // Allocate and initialize a new j.l.Integer.
3181 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
3182 // JIT object table.
3183 uint32_t address =
3184 dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
3185 __ LoadConst32(calling_convention.GetRegisterAt(0), address);
3186 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3187 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3188 __ StoreConstToOffset(kStoreWord, value, out, info.value_offset, TMP);
3189 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3190 // one.
3191 icodegen->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3192 }
3193 } else {
3194 Register in = locations->InAt(0).AsRegister<Register>();
3195 MipsLabel allocate, done;
3196 int32_t count = static_cast<uint32_t>(info.high) - info.low + 1;
3197
3198 // Is (info.low <= in) && (in <= info.high)?
3199 __ Addiu32(out, in, -info.low);
3200 // As unsigned quantities is out < (info.high - info.low + 1)?
3201 if (IsInt<16>(count)) {
3202 __ Sltiu(AT, out, count);
3203 } else {
3204 __ LoadConst32(AT, count);
3205 __ Sltu(AT, out, AT);
3206 }
3207 // Branch if out >= (info.high - info.low + 1).
3208 // This means that "in" is outside of the range [info.low, info.high].
3209 __ Beqz(AT, &allocate);
3210
3211 // If the value is within the bounds, load the j.l.Integer directly from the array.
3212 uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3213 uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
3214 __ LoadConst32(TMP, data_offset + address);
3215 __ ShiftAndAdd(out, out, TMP, TIMES_4);
3216 __ Lw(out, out, 0);
3217 __ MaybeUnpoisonHeapReference(out);
3218 __ B(&done);
3219
3220 __ Bind(&allocate);
3221 // Otherwise allocate and initialize a new j.l.Integer.
3222 address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
3223 __ LoadConst32(calling_convention.GetRegisterAt(0), address);
3224 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3225 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3226 __ StoreToOffset(kStoreWord, in, out, info.value_offset);
3227 // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
3228 // one.
3229 icodegen->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3230 __ Bind(&done);
3231 }
3232}
3233
Chris Larsen2714fe62016-02-11 14:23:53 -08003234// Unimplemented intrinsics.
3235
Aart Bik2f9fcc92016-03-01 15:16:54 -08003236UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil)
3237UNIMPLEMENTED_INTRINSIC(MIPS, MathFloor)
3238UNIMPLEMENTED_INTRINSIC(MIPS, MathRint)
3239UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble)
Alexey Frunze15958152017-02-09 19:08:30 -08003240UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLongVolatile);
3241UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongVolatile);
Aart Bik2f9fcc92016-03-01 15:16:54 -08003242UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
Chris Larsen701566a2015-10-27 15:29:13 -07003243
Aart Bik2f9fcc92016-03-01 15:16:54 -08003244UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
Aart Bik2f9fcc92016-03-01 15:16:54 -08003245UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
Aart Bik3f67e692016-01-15 14:35:12 -08003246
Aart Bikff7d89c2016-11-07 08:49:28 -08003247UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
3248UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08003249UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferAppend);
3250UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferLength);
3251UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferToString);
3252UNIMPLEMENTED_INTRINSIC(MIPS, StringBuilderAppend);
3253UNIMPLEMENTED_INTRINSIC(MIPS, StringBuilderLength);
3254UNIMPLEMENTED_INTRINSIC(MIPS, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08003255
Aart Bik0e54c012016-03-04 12:08:31 -08003256// 1.8.
3257UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddInt)
3258UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddLong)
3259UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetInt)
3260UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong)
3261UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject)
Chris Larsen701566a2015-10-27 15:29:13 -07003262
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003263UNIMPLEMENTED_INTRINSIC(MIPS, ThreadInterrupted)
3264
Aart Bik0e54c012016-03-04 12:08:31 -08003265UNREACHABLE_INTRINSICS(MIPS)
Chris Larsen2714fe62016-02-11 14:23:53 -08003266
Chris Larsen701566a2015-10-27 15:29:13 -07003267#undef __
3268
3269} // namespace mips
3270} // namespace art