intrinsics_mips64.cc revision 806f0122e923581f559043e82cf958bab5defc87
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "intrinsics_mips64.h"
18
19#include "arch/mips64/instruction_set_features_mips64.h"
20#include "art_method.h"
21#include "code_generator_mips64.h"
22#include "entrypoints/quick/quick_entrypoints.h"
23#include "intrinsics.h"
24#include "mirror/array-inl.h"
25#include "mirror/string.h"
26#include "thread.h"
27#include "utils/mips64/assembler_mips64.h"
28#include "utils/mips64/constants_mips64.h"
29
30namespace art {
31
32namespace mips64 {
33
34IntrinsicLocationsBuilderMIPS64::IntrinsicLocationsBuilderMIPS64(CodeGeneratorMIPS64* codegen)
35  : arena_(codegen->GetGraph()->GetArena()) {
36}
37
38Mips64Assembler* IntrinsicCodeGeneratorMIPS64::GetAssembler() {
39  return reinterpret_cast<Mips64Assembler*>(codegen_->GetAssembler());
40}
41
42ArenaAllocator* IntrinsicCodeGeneratorMIPS64::GetAllocator() {
43  return codegen_->GetGraph()->GetArena();
44}
45
46#define __ codegen->GetAssembler()->
47
48static void MoveFromReturnRegister(Location trg,
49                                   Primitive::Type type,
50                                   CodeGeneratorMIPS64* codegen) {
51  if (!trg.IsValid()) {
52    DCHECK_EQ(type, Primitive::kPrimVoid);
53    return;
54  }
55
56  DCHECK_NE(type, Primitive::kPrimVoid);
57
58  if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) {
59    GpuRegister trg_reg = trg.AsRegister<GpuRegister>();
60    if (trg_reg != V0) {
61      __ Move(V0, trg_reg);
62    }
63  } else {
64    FpuRegister trg_reg = trg.AsFpuRegister<FpuRegister>();
65    if (trg_reg != F0) {
66      if (type == Primitive::kPrimFloat) {
67        __ MovS(F0, trg_reg);
68      } else {
69        __ MovD(F0, trg_reg);
70      }
71    }
72  }
73}
74
75static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS64* codegen) {
76  InvokeDexCallingConventionVisitorMIPS64 calling_convention_visitor;
77  IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
78}
79
80// Slow-path for fallback (calling the managed code to handle the
81// intrinsic) in an intrinsified call. This will copy the arguments
82// into the positions for a regular call.
83//
84// Note: The actual parameters are required to be in the locations
85//       given by the invoke's location summary. If an intrinsic
86//       modifies those locations before a slowpath call, they must be
87//       restored!
88class IntrinsicSlowPathMIPS64 : public SlowPathCodeMIPS64 {
89 public:
90  explicit IntrinsicSlowPathMIPS64(HInvoke* invoke)
91     : SlowPathCodeMIPS64(invoke), invoke_(invoke) { }
92
93  void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
94    CodeGeneratorMIPS64* codegen = down_cast<CodeGeneratorMIPS64*>(codegen_in);
95
96    __ Bind(GetEntryLabel());
97
98    SaveLiveRegisters(codegen, invoke_->GetLocations());
99
100    MoveArguments(invoke_, codegen);
101
102    if (invoke_->IsInvokeStaticOrDirect()) {
103      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
104                                          Location::RegisterLocation(A0));
105    } else {
106      codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), Location::RegisterLocation(A0));
107    }
108    codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
109
110    // Copy the result back to the expected output.
111    Location out = invoke_->GetLocations()->Out();
112    if (out.IsValid()) {
113      DCHECK(out.IsRegister());  // TODO: Replace this when we support output in memory.
114      DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
115      MoveFromReturnRegister(out, invoke_->GetType(), codegen);
116    }
117
118    RestoreLiveRegisters(codegen, invoke_->GetLocations());
119    __ Bc(GetExitLabel());
120  }
121
122  const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathMIPS64"; }
123
124 private:
125  // The instruction where this slow path is happening.
126  HInvoke* const invoke_;
127
128  DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathMIPS64);
129};
130
131#undef __
132
133bool IntrinsicLocationsBuilderMIPS64::TryDispatch(HInvoke* invoke) {
134  Dispatch(invoke);
135  LocationSummary* res = invoke->GetLocations();
136  return res != nullptr && res->Intrinsified();
137}
138
139#define __ assembler->
140
141static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
142  LocationSummary* locations = new (arena) LocationSummary(invoke,
143                                                           LocationSummary::kNoCall,
144                                                           kIntrinsified);
145  locations->SetInAt(0, Location::RequiresFpuRegister());
146  locations->SetOut(Location::RequiresRegister());
147}
148
149static void MoveFPToInt(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) {
150  FpuRegister in  = locations->InAt(0).AsFpuRegister<FpuRegister>();
151  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
152
153  if (is64bit) {
154    __ Dmfc1(out, in);
155  } else {
156    __ Mfc1(out, in);
157  }
158}
159
160// long java.lang.Double.doubleToRawLongBits(double)
161void IntrinsicLocationsBuilderMIPS64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
162  CreateFPToIntLocations(arena_, invoke);
163}
164
165void IntrinsicCodeGeneratorMIPS64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
166  MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
167}
168
169// int java.lang.Float.floatToRawIntBits(float)
170void IntrinsicLocationsBuilderMIPS64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
171  CreateFPToIntLocations(arena_, invoke);
172}
173
174void IntrinsicCodeGeneratorMIPS64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
175  MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
176}
177
178static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
179  LocationSummary* locations = new (arena) LocationSummary(invoke,
180                                                           LocationSummary::kNoCall,
181                                                           kIntrinsified);
182  locations->SetInAt(0, Location::RequiresRegister());
183  locations->SetOut(Location::RequiresFpuRegister());
184}
185
186static void MoveIntToFP(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) {
187  GpuRegister in  = locations->InAt(0).AsRegister<GpuRegister>();
188  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
189
190  if (is64bit) {
191    __ Dmtc1(in, out);
192  } else {
193    __ Mtc1(in, out);
194  }
195}
196
197// double java.lang.Double.longBitsToDouble(long)
198void IntrinsicLocationsBuilderMIPS64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
199  CreateIntToFPLocations(arena_, invoke);
200}
201
202void IntrinsicCodeGeneratorMIPS64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
203  MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
204}
205
206// float java.lang.Float.intBitsToFloat(int)
207void IntrinsicLocationsBuilderMIPS64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
208  CreateIntToFPLocations(arena_, invoke);
209}
210
211void IntrinsicCodeGeneratorMIPS64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
212  MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
213}
214
215static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
216  LocationSummary* locations = new (arena) LocationSummary(invoke,
217                                                           LocationSummary::kNoCall,
218                                                           kIntrinsified);
219  locations->SetInAt(0, Location::RequiresRegister());
220  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
221}
222
223static void GenReverseBytes(LocationSummary* locations,
224                            Primitive::Type type,
225                            Mips64Assembler* assembler) {
226  GpuRegister in  = locations->InAt(0).AsRegister<GpuRegister>();
227  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
228
229  switch (type) {
230    case Primitive::kPrimShort:
231      __ Dsbh(out, in);
232      __ Seh(out, out);
233      break;
234    case Primitive::kPrimInt:
235      __ Rotr(out, in, 16);
236      __ Wsbh(out, out);
237      break;
238    case Primitive::kPrimLong:
239      __ Dsbh(out, in);
240      __ Dshd(out, out);
241      break;
242    default:
243      LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
244      UNREACHABLE();
245  }
246}
247
248// int java.lang.Integer.reverseBytes(int)
249void IntrinsicLocationsBuilderMIPS64::VisitIntegerReverseBytes(HInvoke* invoke) {
250  CreateIntToIntLocations(arena_, invoke);
251}
252
253void IntrinsicCodeGeneratorMIPS64::VisitIntegerReverseBytes(HInvoke* invoke) {
254  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
255}
256
257// long java.lang.Long.reverseBytes(long)
258void IntrinsicLocationsBuilderMIPS64::VisitLongReverseBytes(HInvoke* invoke) {
259  CreateIntToIntLocations(arena_, invoke);
260}
261
262void IntrinsicCodeGeneratorMIPS64::VisitLongReverseBytes(HInvoke* invoke) {
263  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
264}
265
266// short java.lang.Short.reverseBytes(short)
267void IntrinsicLocationsBuilderMIPS64::VisitShortReverseBytes(HInvoke* invoke) {
268  CreateIntToIntLocations(arena_, invoke);
269}
270
271void IntrinsicCodeGeneratorMIPS64::VisitShortReverseBytes(HInvoke* invoke) {
272  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
273}
274
275static void GenNumberOfLeadingZeroes(LocationSummary* locations,
276                                     bool is64bit,
277                                     Mips64Assembler* assembler) {
278  GpuRegister in  = locations->InAt(0).AsRegister<GpuRegister>();
279  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
280
281  if (is64bit) {
282    __ Dclz(out, in);
283  } else {
284    __ Clz(out, in);
285  }
286}
287
288// int java.lang.Integer.numberOfLeadingZeros(int i)
289void IntrinsicLocationsBuilderMIPS64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
290  CreateIntToIntLocations(arena_, invoke);
291}
292
293void IntrinsicCodeGeneratorMIPS64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
294  GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
295}
296
297// int java.lang.Long.numberOfLeadingZeros(long i)
298void IntrinsicLocationsBuilderMIPS64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
299  CreateIntToIntLocations(arena_, invoke);
300}
301
302void IntrinsicCodeGeneratorMIPS64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
303  GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
304}
305
306static void GenNumberOfTrailingZeroes(LocationSummary* locations,
307                                      bool is64bit,
308                                      Mips64Assembler* assembler) {
309  Location in = locations->InAt(0);
310  Location out = locations->Out();
311
312  if (is64bit) {
313    __ Dsbh(out.AsRegister<GpuRegister>(), in.AsRegister<GpuRegister>());
314    __ Dshd(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>());
315    __ Dbitswap(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>());
316    __ Dclz(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>());
317  } else {
318    __ Rotr(out.AsRegister<GpuRegister>(), in.AsRegister<GpuRegister>(), 16);
319    __ Wsbh(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>());
320    __ Bitswap(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>());
321    __ Clz(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>());
322  }
323}
324
325// int java.lang.Integer.numberOfTrailingZeros(int i)
326void IntrinsicLocationsBuilderMIPS64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
327  CreateIntToIntLocations(arena_, invoke);
328}
329
330void IntrinsicCodeGeneratorMIPS64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
331  GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
332}
333
334// int java.lang.Long.numberOfTrailingZeros(long i)
335void IntrinsicLocationsBuilderMIPS64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
336  CreateIntToIntLocations(arena_, invoke);
337}
338
339void IntrinsicCodeGeneratorMIPS64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
340  GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
341}
342
343static void GenReverse(LocationSummary* locations,
344                       Primitive::Type type,
345                       Mips64Assembler* assembler) {
346  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
347
348  GpuRegister in  = locations->InAt(0).AsRegister<GpuRegister>();
349  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
350
351  if (type == Primitive::kPrimInt) {
352    __ Rotr(out, in, 16);
353    __ Wsbh(out, out);
354    __ Bitswap(out, out);
355  } else {
356    __ Dsbh(out, in);
357    __ Dshd(out, out);
358    __ Dbitswap(out, out);
359  }
360}
361
362// int java.lang.Integer.reverse(int)
363void IntrinsicLocationsBuilderMIPS64::VisitIntegerReverse(HInvoke* invoke) {
364  CreateIntToIntLocations(arena_, invoke);
365}
366
367void IntrinsicCodeGeneratorMIPS64::VisitIntegerReverse(HInvoke* invoke) {
368  GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
369}
370
371// long java.lang.Long.reverse(long)
372void IntrinsicLocationsBuilderMIPS64::VisitLongReverse(HInvoke* invoke) {
373  CreateIntToIntLocations(arena_, invoke);
374}
375
376void IntrinsicCodeGeneratorMIPS64::VisitLongReverse(HInvoke* invoke) {
377  GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
378}
379
380static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
381  LocationSummary* locations = new (arena) LocationSummary(invoke,
382                                                           LocationSummary::kNoCall,
383                                                           kIntrinsified);
384  locations->SetInAt(0, Location::RequiresFpuRegister());
385  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
386}
387
388static void GenBitCount(LocationSummary* locations,
389                        const Primitive::Type type,
390                        Mips64Assembler* assembler) {
391  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
392  GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>();
393
394  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
395
396  // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
397  //
398  // A generalization of the best bit counting method to integers of
399  // bit-widths up to 128 (parameterized by type T) is this:
400  //
401  // v = v - ((v >> 1) & (T)~(T)0/3);                           // temp
402  // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3);      // temp
403  // v = (v + (v >> 4)) & (T)~(T)0/255*15;                      // temp
404  // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; // count
405  //
406  // For comparison, for 32-bit quantities, this algorithm can be executed
407  // using 20 MIPS instructions (the calls to LoadConst32() generate two
408  // machine instructions each for the values being used in this algorithm).
409  // A(n unrolled) loop-based algorithm requires 25 instructions.
410  //
411  // For a 64-bit operand this can be performed in 24 instructions compared
412  // to a(n unrolled) loop based algorithm which requires 38 instructions.
413  //
414  // There are algorithms which are faster in the cases where very few
415  // bits are set but the algorithm here attempts to minimize the total
416  // number of instructions executed even when a large number of bits
417  // are set.
418
419  if (type == Primitive::kPrimInt) {
420    __ Srl(TMP, in, 1);
421    __ LoadConst32(AT, 0x55555555);
422    __ And(TMP, TMP, AT);
423    __ Subu(TMP, in, TMP);
424    __ LoadConst32(AT, 0x33333333);
425    __ And(out, TMP, AT);
426    __ Srl(TMP, TMP, 2);
427    __ And(TMP, TMP, AT);
428    __ Addu(TMP, out, TMP);
429    __ Srl(out, TMP, 4);
430    __ Addu(out, out, TMP);
431    __ LoadConst32(AT, 0x0F0F0F0F);
432    __ And(out, out, AT);
433    __ LoadConst32(TMP, 0x01010101);
434    __ MulR6(out, out, TMP);
435    __ Srl(out, out, 24);
436  } else if (type == Primitive::kPrimLong) {
437    __ Dsrl(TMP, in, 1);
438    __ LoadConst64(AT, 0x5555555555555555L);
439    __ And(TMP, TMP, AT);
440    __ Dsubu(TMP, in, TMP);
441    __ LoadConst64(AT, 0x3333333333333333L);
442    __ And(out, TMP, AT);
443    __ Dsrl(TMP, TMP, 2);
444    __ And(TMP, TMP, AT);
445    __ Daddu(TMP, out, TMP);
446    __ Dsrl(out, TMP, 4);
447    __ Daddu(out, out, TMP);
448    __ LoadConst64(AT, 0x0F0F0F0F0F0F0F0FL);
449    __ And(out, out, AT);
450    __ LoadConst64(TMP, 0x0101010101010101L);
451    __ Dmul(out, out, TMP);
452    __ Dsrl32(out, out, 24);
453  }
454}
455
456// int java.lang.Integer.bitCount(int)
457void IntrinsicLocationsBuilderMIPS64::VisitIntegerBitCount(HInvoke* invoke) {
458  CreateIntToIntLocations(arena_, invoke);
459}
460
461void IntrinsicCodeGeneratorMIPS64::VisitIntegerBitCount(HInvoke* invoke) {
462  GenBitCount(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
463}
464
465// int java.lang.Long.bitCount(long)
466void IntrinsicLocationsBuilderMIPS64::VisitLongBitCount(HInvoke* invoke) {
467  CreateIntToIntLocations(arena_, invoke);
468}
469
470void IntrinsicCodeGeneratorMIPS64::VisitLongBitCount(HInvoke* invoke) {
471  GenBitCount(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
472}
473
474static void MathAbsFP(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) {
475  FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
476  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
477
478  if (is64bit) {
479    __ AbsD(out, in);
480  } else {
481    __ AbsS(out, in);
482  }
483}
484
485// double java.lang.Math.abs(double)
486void IntrinsicLocationsBuilderMIPS64::VisitMathAbsDouble(HInvoke* invoke) {
487  CreateFPToFPLocations(arena_, invoke);
488}
489
490void IntrinsicCodeGeneratorMIPS64::VisitMathAbsDouble(HInvoke* invoke) {
491  MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
492}
493
494// float java.lang.Math.abs(float)
495void IntrinsicLocationsBuilderMIPS64::VisitMathAbsFloat(HInvoke* invoke) {
496  CreateFPToFPLocations(arena_, invoke);
497}
498
499void IntrinsicCodeGeneratorMIPS64::VisitMathAbsFloat(HInvoke* invoke) {
500  MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
501}
502
503static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
504  LocationSummary* locations = new (arena) LocationSummary(invoke,
505                                                           LocationSummary::kNoCall,
506                                                           kIntrinsified);
507  locations->SetInAt(0, Location::RequiresRegister());
508  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
509}
510
511static void GenAbsInteger(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) {
512  GpuRegister in  = locations->InAt(0).AsRegister<GpuRegister>();
513  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
514
515  if (is64bit) {
516    __ Dsra32(AT, in, 31);
517    __ Xor(out, in, AT);
518    __ Dsubu(out, out, AT);
519  } else {
520    __ Sra(AT, in, 31);
521    __ Xor(out, in, AT);
522    __ Subu(out, out, AT);
523  }
524}
525
526// int java.lang.Math.abs(int)
527void IntrinsicLocationsBuilderMIPS64::VisitMathAbsInt(HInvoke* invoke) {
528  CreateIntToInt(arena_, invoke);
529}
530
531void IntrinsicCodeGeneratorMIPS64::VisitMathAbsInt(HInvoke* invoke) {
532  GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
533}
534
535// long java.lang.Math.abs(long)
536void IntrinsicLocationsBuilderMIPS64::VisitMathAbsLong(HInvoke* invoke) {
537  CreateIntToInt(arena_, invoke);
538}
539
540void IntrinsicCodeGeneratorMIPS64::VisitMathAbsLong(HInvoke* invoke) {
541  GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
542}
543
544static void GenMinMaxFP(LocationSummary* locations,
545                        bool is_min,
546                        Primitive::Type type,
547                        Mips64Assembler* assembler) {
548  FpuRegister a = locations->InAt(0).AsFpuRegister<FpuRegister>();
549  FpuRegister b = locations->InAt(1).AsFpuRegister<FpuRegister>();
550  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
551
552  Mips64Label noNaNs;
553  Mips64Label done;
554  FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
555
556  // When Java computes min/max it prefers a NaN to a number; the
557  // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
558  // the inputs is a NaN and the other is a valid number, the MIPS
559  // instruction will return the number; Java wants the NaN value
560  // returned. This is why there is extra logic preceding the use of
561  // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
562  // NaN, return the NaN, otherwise return the min/max.
563  if (type == Primitive::kPrimDouble) {
564    __ CmpUnD(FTMP, a, b);
565    __ Bc1eqz(FTMP, &noNaNs);
566
567    // One of the inputs is a NaN
568    __ CmpEqD(ftmp, a, a);
569    // If a == a then b is the NaN, otherwise a is the NaN.
570    __ SelD(ftmp, a, b);
571
572    if (ftmp != out) {
573      __ MovD(out, ftmp);
574    }
575
576    __ Bc(&done);
577
578    __ Bind(&noNaNs);
579
580    if (is_min) {
581      __ MinD(out, a, b);
582    } else {
583      __ MaxD(out, a, b);
584    }
585  } else {
586    DCHECK_EQ(type, Primitive::kPrimFloat);
587    __ CmpUnS(FTMP, a, b);
588    __ Bc1eqz(FTMP, &noNaNs);
589
590    // One of the inputs is a NaN
591    __ CmpEqS(ftmp, a, a);
592    // If a == a then b is the NaN, otherwise a is the NaN.
593    __ SelS(ftmp, a, b);
594
595    if (ftmp != out) {
596      __ MovS(out, ftmp);
597    }
598
599    __ Bc(&done);
600
601    __ Bind(&noNaNs);
602
603    if (is_min) {
604      __ MinS(out, a, b);
605    } else {
606      __ MaxS(out, a, b);
607    }
608  }
609
610  __ Bind(&done);
611}
612
613static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
614  LocationSummary* locations = new (arena) LocationSummary(invoke,
615                                                           LocationSummary::kNoCall,
616                                                           kIntrinsified);
617  locations->SetInAt(0, Location::RequiresFpuRegister());
618  locations->SetInAt(1, Location::RequiresFpuRegister());
619  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
620}
621
622// double java.lang.Math.min(double, double)
623void IntrinsicLocationsBuilderMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) {
624  CreateFPFPToFPLocations(arena_, invoke);
625}
626
627void IntrinsicCodeGeneratorMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) {
628  GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, Primitive::kPrimDouble, GetAssembler());
629}
630
631// float java.lang.Math.min(float, float)
632void IntrinsicLocationsBuilderMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) {
633  CreateFPFPToFPLocations(arena_, invoke);
634}
635
636void IntrinsicCodeGeneratorMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) {
637  GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, Primitive::kPrimFloat, GetAssembler());
638}
639
640// double java.lang.Math.max(double, double)
641void IntrinsicLocationsBuilderMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
642  CreateFPFPToFPLocations(arena_, invoke);
643}
644
645void IntrinsicCodeGeneratorMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
646  GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, Primitive::kPrimDouble, GetAssembler());
647}
648
649// float java.lang.Math.max(float, float)
650void IntrinsicLocationsBuilderMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) {
651  CreateFPFPToFPLocations(arena_, invoke);
652}
653
654void IntrinsicCodeGeneratorMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) {
655  GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, Primitive::kPrimFloat, GetAssembler());
656}
657
658static void GenMinMax(LocationSummary* locations,
659                      bool is_min,
660                      Mips64Assembler* assembler) {
661  GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
662  GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>();
663  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
664
665  if (lhs == rhs) {
666    if (out != lhs) {
667      __ Move(out, lhs);
668    }
669  } else {
670    // Some architectures, such as ARM and MIPS (prior to r6), have a
671    // conditional move instruction which only changes the target
672    // (output) register if the condition is true (MIPS prior to r6 had
673    // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always
674    // change the target (output) register.  If the condition is true the
675    // output register gets the contents of the "rs" register; otherwise,
676    // the output register is set to zero. One consequence of this is
677    // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6
678    // needs to use a pair of SELEQZ/SELNEZ instructions.  After
679    // executing this pair of instructions one of the output registers
680    // from the pair will necessarily contain zero. Then the code ORs the
681    // output registers from the SELEQZ/SELNEZ instructions to get the
682    // final result.
683    //
684    // The initial test to see if the output register is same as the
685    // first input register is needed to make sure that value in the
686    // first input register isn't clobbered before we've finished
687    // computing the output value. The logic in the corresponding else
688    // clause performs the same task but makes sure the second input
689    // register isn't clobbered in the event that it's the same register
690    // as the output register; the else clause also handles the case
691    // where the output register is distinct from both the first, and the
692    // second input registers.
693    if (out == lhs) {
694      __ Slt(AT, rhs, lhs);
695      if (is_min) {
696        __ Seleqz(out, lhs, AT);
697        __ Selnez(AT, rhs, AT);
698      } else {
699        __ Selnez(out, lhs, AT);
700        __ Seleqz(AT, rhs, AT);
701      }
702    } else {
703      __ Slt(AT, lhs, rhs);
704      if (is_min) {
705        __ Seleqz(out, rhs, AT);
706        __ Selnez(AT, lhs, AT);
707      } else {
708        __ Selnez(out, rhs, AT);
709        __ Seleqz(AT, lhs, AT);
710      }
711    }
712    __ Or(out, out, AT);
713  }
714}
715
716static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
717  LocationSummary* locations = new (arena) LocationSummary(invoke,
718                                                           LocationSummary::kNoCall,
719                                                           kIntrinsified);
720  locations->SetInAt(0, Location::RequiresRegister());
721  locations->SetInAt(1, Location::RequiresRegister());
722  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
723}
724
725// int java.lang.Math.min(int, int)
726void IntrinsicLocationsBuilderMIPS64::VisitMathMinIntInt(HInvoke* invoke) {
727  CreateIntIntToIntLocations(arena_, invoke);
728}
729
730void IntrinsicCodeGeneratorMIPS64::VisitMathMinIntInt(HInvoke* invoke) {
731  GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler());
732}
733
734// long java.lang.Math.min(long, long)
735void IntrinsicLocationsBuilderMIPS64::VisitMathMinLongLong(HInvoke* invoke) {
736  CreateIntIntToIntLocations(arena_, invoke);
737}
738
739void IntrinsicCodeGeneratorMIPS64::VisitMathMinLongLong(HInvoke* invoke) {
740  GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler());
741}
742
743// int java.lang.Math.max(int, int)
744void IntrinsicLocationsBuilderMIPS64::VisitMathMaxIntInt(HInvoke* invoke) {
745  CreateIntIntToIntLocations(arena_, invoke);
746}
747
748void IntrinsicCodeGeneratorMIPS64::VisitMathMaxIntInt(HInvoke* invoke) {
749  GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler());
750}
751
752// long java.lang.Math.max(long, long)
753void IntrinsicLocationsBuilderMIPS64::VisitMathMaxLongLong(HInvoke* invoke) {
754  CreateIntIntToIntLocations(arena_, invoke);
755}
756
757void IntrinsicCodeGeneratorMIPS64::VisitMathMaxLongLong(HInvoke* invoke) {
758  GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler());
759}
760
761// double java.lang.Math.sqrt(double)
762void IntrinsicLocationsBuilderMIPS64::VisitMathSqrt(HInvoke* invoke) {
763  CreateFPToFPLocations(arena_, invoke);
764}
765
766void IntrinsicCodeGeneratorMIPS64::VisitMathSqrt(HInvoke* invoke) {
767  LocationSummary* locations = invoke->GetLocations();
768  Mips64Assembler* assembler = GetAssembler();
769  FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
770  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
771
772  __ SqrtD(out, in);
773}
774
775static void CreateFPToFP(ArenaAllocator* arena,
776                         HInvoke* invoke,
777                         Location::OutputOverlap overlaps = Location::kOutputOverlap) {
778  LocationSummary* locations = new (arena) LocationSummary(invoke,
779                                                           LocationSummary::kNoCall,
780                                                           kIntrinsified);
781  locations->SetInAt(0, Location::RequiresFpuRegister());
782  locations->SetOut(Location::RequiresFpuRegister(), overlaps);
783}
784
785// double java.lang.Math.rint(double)
786void IntrinsicLocationsBuilderMIPS64::VisitMathRint(HInvoke* invoke) {
787  CreateFPToFP(arena_, invoke, Location::kNoOutputOverlap);
788}
789
790void IntrinsicCodeGeneratorMIPS64::VisitMathRint(HInvoke* invoke) {
791  LocationSummary* locations = invoke->GetLocations();
792  Mips64Assembler* assembler = GetAssembler();
793  FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
794  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
795
796  __ RintD(out, in);
797}
798
799// double java.lang.Math.floor(double)
800void IntrinsicLocationsBuilderMIPS64::VisitMathFloor(HInvoke* invoke) {
801  CreateFPToFP(arena_, invoke);
802}
803
804const constexpr uint16_t kFPLeaveUnchanged = kPositiveZero |
805                                             kPositiveInfinity |
806                                             kNegativeZero |
807                                             kNegativeInfinity |
808                                             kQuietNaN |
809                                             kSignalingNaN;
810
811enum FloatRoundingMode {
812  kFloor,
813  kCeil,
814};
815
816static void GenRoundingMode(LocationSummary* locations,
817                            FloatRoundingMode mode,
818                            Mips64Assembler* assembler) {
819  FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
820  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
821
822  DCHECK_NE(in, out);
823
824  Mips64Label done;
825
826  // double floor/ceil(double in) {
827  //     if in.isNaN || in.isInfinite || in.isZero {
828  //         return in;
829  //     }
830  __ ClassD(out, in);
831  __ Dmfc1(AT, out);
832  __ Andi(AT, AT, kFPLeaveUnchanged);   // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
833  __ MovD(out, in);
834  __ Bnezc(AT, &done);
835
836  //     Long outLong = floor/ceil(in);
837  //     if outLong == Long.MAX_VALUE {
838  //         // floor()/ceil() has almost certainly returned a value
839  //         // which can't be successfully represented as a signed
840  //         // 64-bit number.  Java expects that the input value will
841  //         // be returned in these cases.
842  //         // There is also a small probability that floor(in)/ceil(in)
843  //         // correctly truncates/rounds up the input value to
844  //         // Long.MAX_VALUE.  In that case, this exception handling
845  //         // code still does the correct thing.
846  //         return in;
847  //     }
848  if (mode == kFloor) {
849    __ FloorLD(out, in);
850  } else  if (mode == kCeil) {
851    __ CeilLD(out, in);
852  }
853  __ Dmfc1(AT, out);
854  __ MovD(out, in);
855  __ LoadConst64(TMP, kPrimLongMax);
856  __ Beqc(AT, TMP, &done);
857
858  //     double out = outLong;
859  //     return out;
860  __ Dmtc1(AT, out);
861  __ Cvtdl(out, out);
862  __ Bind(&done);
863  // }
864}
865
866void IntrinsicCodeGeneratorMIPS64::VisitMathFloor(HInvoke* invoke) {
867  GenRoundingMode(invoke->GetLocations(), kFloor, GetAssembler());
868}
869
870// double java.lang.Math.ceil(double)
871void IntrinsicLocationsBuilderMIPS64::VisitMathCeil(HInvoke* invoke) {
872  CreateFPToFP(arena_, invoke);
873}
874
875void IntrinsicCodeGeneratorMIPS64::VisitMathCeil(HInvoke* invoke) {
876  GenRoundingMode(invoke->GetLocations(), kCeil, GetAssembler());
877}
878
879static void GenRound(LocationSummary* locations, Mips64Assembler* assembler, Primitive::Type type) {
880  FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
881  FpuRegister half = locations->GetTemp(0).AsFpuRegister<FpuRegister>();
882  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
883
884  DCHECK(type == Primitive::kPrimFloat || type == Primitive::kPrimDouble);
885
886  Mips64Label done;
887  Mips64Label finite;
888  Mips64Label add;
889
890  // if (in.isNaN) {
891  //   return 0;
892  // }
893  //
894  // out = floor(in);
895  //
896  // /*
897  //  * TODO: Amend this code when emulator FCSR.NAN2008=1 bug is fixed.
898  //  *
899  //  * Starting with MIPSR6, which always sets FCSR.NAN2008=1, negative
900  //  * numbers which are too large to be represented in a 32-/64-bit
901  //  * signed integer will be processed by floor.X.Y to output
902  //  * Integer.MIN_VALUE/Long.MIN_VALUE, and will no longer be
903  //  * processed by this "if" statement.
904  //  *
905  //  * However, this bug in the 64-bit MIPS emulator causes the
906  //  * behavior of floor.X.Y to be the same as pre-R6 implementations
907  //  * of MIPS64.  When that bug is fixed this logic should be amended.
908  //  */
909  // if (out == MAX_VALUE) {
910  //   TMP = (in < 0.0) ? 1 : 0;
911  //   /*
912  //    * If TMP is 1, then adding it to out will wrap its value from
913  //    * MAX_VALUE to MIN_VALUE.
914  //    */
915  //   return out += TMP;
916  // }
917  //
918  // /*
919  //  * For negative values not handled by the previous "if" statement the
920  //  * test here will correctly set the value of TMP.
921  //  */
922  // TMP = ((in - out) >= 0.5) ? 1 : 0;
923  // return out += TMP;
924
925  // Test for NaN.
926  if (type == Primitive::kPrimDouble) {
927    __ CmpUnD(FTMP, in, in);
928  } else {
929    __ CmpUnS(FTMP, in, in);
930  }
931
932  // Return zero for NaN.
933  __ Move(out, ZERO);
934  __ Bc1nez(FTMP, &done);
935
936  // out = floor(in);
937  if (type == Primitive::kPrimDouble) {
938    __ FloorLD(FTMP, in);
939    __ Dmfc1(out, FTMP);
940  } else {
941    __ FloorWS(FTMP, in);
942    __ Mfc1(out, FTMP);
943  }
944
945  // TMP = (out = java.lang.Integer.MAX_VALUE) ? 1 : 0;
946  if (type == Primitive::kPrimDouble) {
947    __ LoadConst64(AT, std::numeric_limits<int64_t>::max());
948  } else {
949    __ LoadConst32(AT, std::numeric_limits<int32_t>::max());
950  }
951  __ Bnec(AT, out, &finite);
952
953  if (type == Primitive::kPrimDouble) {
954    __ Dmtc1(ZERO, FTMP);
955    __ CmpLtD(FTMP, in, FTMP);
956    __ Dmfc1(AT, FTMP);
957  } else {
958    __ Mtc1(ZERO, FTMP);
959    __ CmpLtS(FTMP, in, FTMP);
960    __ Mfc1(AT, FTMP);
961  }
962
963  __ Bc(&add);
964
965  __ Bind(&finite);
966
967  // TMP = (0.5 <= (in - out)) ? -1 : 0;
968  if (type == Primitive::kPrimDouble) {
969    __ Cvtdl(FTMP, FTMP);  // Convert output of floor.l.d back to "double".
970    __ LoadConst64(AT, bit_cast<int64_t, double>(0.5));
971    __ SubD(FTMP, in, FTMP);
972    __ Dmtc1(AT, half);
973    __ CmpLeD(FTMP, half, FTMP);
974    __ Dmfc1(AT, FTMP);
975  } else {
976    __ Cvtsw(FTMP, FTMP);  // Convert output of floor.w.s back to "float".
977    __ LoadConst32(AT, bit_cast<int32_t, float>(0.5f));
978    __ SubS(FTMP, in, FTMP);
979    __ Mtc1(AT, half);
980    __ CmpLeS(FTMP, half, FTMP);
981    __ Mfc1(AT, FTMP);
982  }
983
984  __ Bind(&add);
985
986  // Return out -= TMP.
987  if (type == Primitive::kPrimDouble) {
988    __ Dsubu(out, out, AT);
989  } else {
990    __ Subu(out, out, AT);
991  }
992
993  __ Bind(&done);
994}
995
996// int java.lang.Math.round(float)
997void IntrinsicLocationsBuilderMIPS64::VisitMathRoundFloat(HInvoke* invoke) {
998  LocationSummary* locations = new (arena_) LocationSummary(invoke,
999                                                           LocationSummary::kNoCall,
1000                                                           kIntrinsified);
1001  locations->SetInAt(0, Location::RequiresFpuRegister());
1002  locations->AddTemp(Location::RequiresFpuRegister());
1003  locations->SetOut(Location::RequiresRegister());
1004}
1005
1006void IntrinsicCodeGeneratorMIPS64::VisitMathRoundFloat(HInvoke* invoke) {
1007  GenRound(invoke->GetLocations(), GetAssembler(), Primitive::kPrimFloat);
1008}
1009
1010// long java.lang.Math.round(double)
1011void IntrinsicLocationsBuilderMIPS64::VisitMathRoundDouble(HInvoke* invoke) {
1012  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1013                                                           LocationSummary::kNoCall,
1014                                                           kIntrinsified);
1015  locations->SetInAt(0, Location::RequiresFpuRegister());
1016  locations->AddTemp(Location::RequiresFpuRegister());
1017  locations->SetOut(Location::RequiresRegister());
1018}
1019
1020void IntrinsicCodeGeneratorMIPS64::VisitMathRoundDouble(HInvoke* invoke) {
1021  GenRound(invoke->GetLocations(), GetAssembler(), Primitive::kPrimDouble);
1022}
1023
1024// byte libcore.io.Memory.peekByte(long address)
1025void IntrinsicLocationsBuilderMIPS64::VisitMemoryPeekByte(HInvoke* invoke) {
1026  CreateIntToIntLocations(arena_, invoke);
1027}
1028
1029void IntrinsicCodeGeneratorMIPS64::VisitMemoryPeekByte(HInvoke* invoke) {
1030  Mips64Assembler* assembler = GetAssembler();
1031  GpuRegister adr = invoke->GetLocations()->InAt(0).AsRegister<GpuRegister>();
1032  GpuRegister out = invoke->GetLocations()->Out().AsRegister<GpuRegister>();
1033
1034  __ Lb(out, adr, 0);
1035}
1036
1037// short libcore.io.Memory.peekShort(long address)
1038void IntrinsicLocationsBuilderMIPS64::VisitMemoryPeekShortNative(HInvoke* invoke) {
1039  CreateIntToIntLocations(arena_, invoke);
1040}
1041
1042void IntrinsicCodeGeneratorMIPS64::VisitMemoryPeekShortNative(HInvoke* invoke) {
1043  Mips64Assembler* assembler = GetAssembler();
1044  GpuRegister adr = invoke->GetLocations()->InAt(0).AsRegister<GpuRegister>();
1045  GpuRegister out = invoke->GetLocations()->Out().AsRegister<GpuRegister>();
1046
1047  __ Lh(out, adr, 0);
1048}
1049
1050// int libcore.io.Memory.peekInt(long address)
1051void IntrinsicLocationsBuilderMIPS64::VisitMemoryPeekIntNative(HInvoke* invoke) {
1052  CreateIntToIntLocations(arena_, invoke);
1053}
1054
1055void IntrinsicCodeGeneratorMIPS64::VisitMemoryPeekIntNative(HInvoke* invoke) {
1056  Mips64Assembler* assembler = GetAssembler();
1057  GpuRegister adr = invoke->GetLocations()->InAt(0).AsRegister<GpuRegister>();
1058  GpuRegister out = invoke->GetLocations()->Out().AsRegister<GpuRegister>();
1059
1060  __ Lw(out, adr, 0);
1061}
1062
1063// long libcore.io.Memory.peekLong(long address)
1064void IntrinsicLocationsBuilderMIPS64::VisitMemoryPeekLongNative(HInvoke* invoke) {
1065  CreateIntToIntLocations(arena_, invoke);
1066}
1067
1068void IntrinsicCodeGeneratorMIPS64::VisitMemoryPeekLongNative(HInvoke* invoke) {
1069  Mips64Assembler* assembler = GetAssembler();
1070  GpuRegister adr = invoke->GetLocations()->InAt(0).AsRegister<GpuRegister>();
1071  GpuRegister out = invoke->GetLocations()->Out().AsRegister<GpuRegister>();
1072
1073  __ Ld(out, adr, 0);
1074}
1075
1076static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
1077  LocationSummary* locations = new (arena) LocationSummary(invoke,
1078                                                           LocationSummary::kNoCall,
1079                                                           kIntrinsified);
1080  locations->SetInAt(0, Location::RequiresRegister());
1081  locations->SetInAt(1, Location::RequiresRegister());
1082}
1083
1084// void libcore.io.Memory.pokeByte(long address, byte value)
1085void IntrinsicLocationsBuilderMIPS64::VisitMemoryPokeByte(HInvoke* invoke) {
1086  CreateIntIntToVoidLocations(arena_, invoke);
1087}
1088
1089void IntrinsicCodeGeneratorMIPS64::VisitMemoryPokeByte(HInvoke* invoke) {
1090  Mips64Assembler* assembler = GetAssembler();
1091  GpuRegister adr = invoke->GetLocations()->InAt(0).AsRegister<GpuRegister>();
1092  GpuRegister val = invoke->GetLocations()->InAt(1).AsRegister<GpuRegister>();
1093
1094  __ Sb(val, adr, 0);
1095}
1096
1097// void libcore.io.Memory.pokeShort(long address, short value)
1098void IntrinsicLocationsBuilderMIPS64::VisitMemoryPokeShortNative(HInvoke* invoke) {
1099  CreateIntIntToVoidLocations(arena_, invoke);
1100}
1101
1102void IntrinsicCodeGeneratorMIPS64::VisitMemoryPokeShortNative(HInvoke* invoke) {
1103  Mips64Assembler* assembler = GetAssembler();
1104  GpuRegister adr = invoke->GetLocations()->InAt(0).AsRegister<GpuRegister>();
1105  GpuRegister val = invoke->GetLocations()->InAt(1).AsRegister<GpuRegister>();
1106
1107  __ Sh(val, adr, 0);
1108}
1109
1110// void libcore.io.Memory.pokeInt(long address, int value)
1111void IntrinsicLocationsBuilderMIPS64::VisitMemoryPokeIntNative(HInvoke* invoke) {
1112  CreateIntIntToVoidLocations(arena_, invoke);
1113}
1114
1115void IntrinsicCodeGeneratorMIPS64::VisitMemoryPokeIntNative(HInvoke* invoke) {
1116  Mips64Assembler* assembler = GetAssembler();
1117  GpuRegister adr = invoke->GetLocations()->InAt(0).AsRegister<GpuRegister>();
1118  GpuRegister val = invoke->GetLocations()->InAt(1).AsRegister<GpuRegister>();
1119
1120  __ Sw(val, adr, 00);
1121}
1122
1123// void libcore.io.Memory.pokeLong(long address, long value)
1124void IntrinsicLocationsBuilderMIPS64::VisitMemoryPokeLongNative(HInvoke* invoke) {
1125  CreateIntIntToVoidLocations(arena_, invoke);
1126}
1127
1128void IntrinsicCodeGeneratorMIPS64::VisitMemoryPokeLongNative(HInvoke* invoke) {
1129  Mips64Assembler* assembler = GetAssembler();
1130  GpuRegister adr = invoke->GetLocations()->InAt(0).AsRegister<GpuRegister>();
1131  GpuRegister val = invoke->GetLocations()->InAt(1).AsRegister<GpuRegister>();
1132
1133  __ Sd(val, adr, 0);
1134}
1135
1136// Thread java.lang.Thread.currentThread()
1137void IntrinsicLocationsBuilderMIPS64::VisitThreadCurrentThread(HInvoke* invoke) {
1138  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1139                                                            LocationSummary::kNoCall,
1140                                                            kIntrinsified);
1141  locations->SetOut(Location::RequiresRegister());
1142}
1143
1144void IntrinsicCodeGeneratorMIPS64::VisitThreadCurrentThread(HInvoke* invoke) {
1145  Mips64Assembler* assembler = GetAssembler();
1146  GpuRegister out = invoke->GetLocations()->Out().AsRegister<GpuRegister>();
1147
1148  __ LoadFromOffset(kLoadUnsignedWord,
1149                    out,
1150                    TR,
1151                    Thread::PeerOffset<kMips64PointerSize>().Int32Value());
1152}
1153
1154static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
1155  LocationSummary* locations = new (arena) LocationSummary(invoke,
1156                                                           LocationSummary::kNoCall,
1157                                                           kIntrinsified);
1158  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
1159  locations->SetInAt(1, Location::RequiresRegister());
1160  locations->SetInAt(2, Location::RequiresRegister());
1161  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1162}
1163
1164static void GenUnsafeGet(HInvoke* invoke,
1165                         Primitive::Type type,
1166                         bool is_volatile,
1167                         CodeGeneratorMIPS64* codegen) {
1168  LocationSummary* locations = invoke->GetLocations();
1169  DCHECK((type == Primitive::kPrimInt) ||
1170         (type == Primitive::kPrimLong) ||
1171         (type == Primitive::kPrimNot));
1172  Mips64Assembler* assembler = codegen->GetAssembler();
1173  // Object pointer.
1174  GpuRegister base = locations->InAt(1).AsRegister<GpuRegister>();
1175  // Long offset.
1176  GpuRegister offset = locations->InAt(2).AsRegister<GpuRegister>();
1177  GpuRegister trg = locations->Out().AsRegister<GpuRegister>();
1178
1179  __ Daddu(TMP, base, offset);
1180  if (is_volatile) {
1181    __ Sync(0);
1182  }
1183  switch (type) {
1184    case Primitive::kPrimInt:
1185      __ Lw(trg, TMP, 0);
1186      break;
1187
1188    case Primitive::kPrimNot:
1189      __ Lwu(trg, TMP, 0);
1190      break;
1191
1192    case Primitive::kPrimLong:
1193      __ Ld(trg, TMP, 0);
1194      break;
1195
1196    default:
1197      LOG(FATAL) << "Unsupported op size " << type;
1198      UNREACHABLE();
1199  }
1200}
1201
1202// int sun.misc.Unsafe.getInt(Object o, long offset)
1203void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGet(HInvoke* invoke) {
1204  CreateIntIntIntToIntLocations(arena_, invoke);
1205}
1206
1207void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGet(HInvoke* invoke) {
1208  GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
1209}
1210
1211// int sun.misc.Unsafe.getIntVolatile(Object o, long offset)
1212void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetVolatile(HInvoke* invoke) {
1213  CreateIntIntIntToIntLocations(arena_, invoke);
1214}
1215
1216void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetVolatile(HInvoke* invoke) {
1217  GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
1218}
1219
1220// long sun.misc.Unsafe.getLong(Object o, long offset)
1221void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetLong(HInvoke* invoke) {
1222  CreateIntIntIntToIntLocations(arena_, invoke);
1223}
1224
1225void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetLong(HInvoke* invoke) {
1226  GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
1227}
1228
1229// long sun.misc.Unsafe.getLongVolatile(Object o, long offset)
1230void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
1231  CreateIntIntIntToIntLocations(arena_, invoke);
1232}
1233
1234void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
1235  GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
1236}
1237
1238// Object sun.misc.Unsafe.getObject(Object o, long offset)
1239void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetObject(HInvoke* invoke) {
1240  CreateIntIntIntToIntLocations(arena_, invoke);
1241}
1242
1243void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetObject(HInvoke* invoke) {
1244  GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
1245}
1246
1247// Object sun.misc.Unsafe.getObjectVolatile(Object o, long offset)
1248void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
1249  CreateIntIntIntToIntLocations(arena_, invoke);
1250}
1251
1252void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
1253  GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
1254}
1255
1256static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
1257  LocationSummary* locations = new (arena) LocationSummary(invoke,
1258                                                           LocationSummary::kNoCall,
1259                                                           kIntrinsified);
1260  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
1261  locations->SetInAt(1, Location::RequiresRegister());
1262  locations->SetInAt(2, Location::RequiresRegister());
1263  locations->SetInAt(3, Location::RequiresRegister());
1264}
1265
1266static void GenUnsafePut(LocationSummary* locations,
1267                         Primitive::Type type,
1268                         bool is_volatile,
1269                         bool is_ordered,
1270                         CodeGeneratorMIPS64* codegen) {
1271  DCHECK((type == Primitive::kPrimInt) ||
1272         (type == Primitive::kPrimLong) ||
1273         (type == Primitive::kPrimNot));
1274  Mips64Assembler* assembler = codegen->GetAssembler();
1275  // Object pointer.
1276  GpuRegister base = locations->InAt(1).AsRegister<GpuRegister>();
1277  // Long offset.
1278  GpuRegister offset = locations->InAt(2).AsRegister<GpuRegister>();
1279  GpuRegister value = locations->InAt(3).AsRegister<GpuRegister>();
1280
1281  __ Daddu(TMP, base, offset);
1282  if (is_volatile || is_ordered) {
1283    __ Sync(0);
1284  }
1285  switch (type) {
1286    case Primitive::kPrimInt:
1287    case Primitive::kPrimNot:
1288      __ Sw(value, TMP, 0);
1289      break;
1290
1291    case Primitive::kPrimLong:
1292      __ Sd(value, TMP, 0);
1293      break;
1294
1295    default:
1296      LOG(FATAL) << "Unsupported op size " << type;
1297      UNREACHABLE();
1298  }
1299  if (is_volatile) {
1300    __ Sync(0);
1301  }
1302
1303  if (type == Primitive::kPrimNot) {
1304    bool value_can_be_null = true;  // TODO: Worth finding out this information?
1305    codegen->MarkGCCard(base, value, value_can_be_null);
1306  }
1307}
1308
1309// void sun.misc.Unsafe.putInt(Object o, long offset, int x)
1310void IntrinsicLocationsBuilderMIPS64::VisitUnsafePut(HInvoke* invoke) {
1311  CreateIntIntIntIntToVoid(arena_, invoke);
1312}
1313
1314void IntrinsicCodeGeneratorMIPS64::VisitUnsafePut(HInvoke* invoke) {
1315  GenUnsafePut(invoke->GetLocations(),
1316               Primitive::kPrimInt,
1317               /* is_volatile */ false,
1318               /* is_ordered */ false,
1319               codegen_);
1320}
1321
1322// void sun.misc.Unsafe.putOrderedInt(Object o, long offset, int x)
1323void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutOrdered(HInvoke* invoke) {
1324  CreateIntIntIntIntToVoid(arena_, invoke);
1325}
1326
1327void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutOrdered(HInvoke* invoke) {
1328  GenUnsafePut(invoke->GetLocations(),
1329               Primitive::kPrimInt,
1330               /* is_volatile */ false,
1331               /* is_ordered */ true,
1332               codegen_);
1333}
1334
1335// void sun.misc.Unsafe.putIntVolatile(Object o, long offset, int x)
1336void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutVolatile(HInvoke* invoke) {
1337  CreateIntIntIntIntToVoid(arena_, invoke);
1338}
1339
1340void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutVolatile(HInvoke* invoke) {
1341  GenUnsafePut(invoke->GetLocations(),
1342               Primitive::kPrimInt,
1343               /* is_volatile */ true,
1344               /* is_ordered */ false,
1345               codegen_);
1346}
1347
1348// void sun.misc.Unsafe.putObject(Object o, long offset, Object x)
1349void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutObject(HInvoke* invoke) {
1350  CreateIntIntIntIntToVoid(arena_, invoke);
1351}
1352
1353void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutObject(HInvoke* invoke) {
1354  GenUnsafePut(invoke->GetLocations(),
1355               Primitive::kPrimNot,
1356               /* is_volatile */ false,
1357               /* is_ordered */ false,
1358               codegen_);
1359}
1360
1361// void sun.misc.Unsafe.putOrderedObject(Object o, long offset, Object x)
1362void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1363  CreateIntIntIntIntToVoid(arena_, invoke);
1364}
1365
1366void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
1367  GenUnsafePut(invoke->GetLocations(),
1368               Primitive::kPrimNot,
1369               /* is_volatile */ false,
1370               /* is_ordered */ true,
1371               codegen_);
1372}
1373
1374// void sun.misc.Unsafe.putObjectVolatile(Object o, long offset, Object x)
1375void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1376  CreateIntIntIntIntToVoid(arena_, invoke);
1377}
1378
1379void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
1380  GenUnsafePut(invoke->GetLocations(),
1381               Primitive::kPrimNot,
1382               /* is_volatile */ true,
1383               /* is_ordered */ false,
1384               codegen_);
1385}
1386
1387// void sun.misc.Unsafe.putLong(Object o, long offset, long x)
1388void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutLong(HInvoke* invoke) {
1389  CreateIntIntIntIntToVoid(arena_, invoke);
1390}
1391
1392void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutLong(HInvoke* invoke) {
1393  GenUnsafePut(invoke->GetLocations(),
1394               Primitive::kPrimLong,
1395               /* is_volatile */ false,
1396               /* is_ordered */ false,
1397               codegen_);
1398}
1399
1400// void sun.misc.Unsafe.putOrderedLong(Object o, long offset, long x)
1401void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1402  CreateIntIntIntIntToVoid(arena_, invoke);
1403}
1404
1405void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
1406  GenUnsafePut(invoke->GetLocations(),
1407               Primitive::kPrimLong,
1408               /* is_volatile */ false,
1409               /* is_ordered */ true,
1410               codegen_);
1411}
1412
1413// void sun.misc.Unsafe.putLongVolatile(Object o, long offset, long x)
1414void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
1415  CreateIntIntIntIntToVoid(arena_, invoke);
1416}
1417
1418void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
1419  GenUnsafePut(invoke->GetLocations(),
1420               Primitive::kPrimLong,
1421               /* is_volatile */ true,
1422               /* is_ordered */ false,
1423               codegen_);
1424}
1425
1426static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
1427  LocationSummary* locations = new (arena) LocationSummary(invoke,
1428                                                           LocationSummary::kNoCall,
1429                                                           kIntrinsified);
1430  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
1431  locations->SetInAt(1, Location::RequiresRegister());
1432  locations->SetInAt(2, Location::RequiresRegister());
1433  locations->SetInAt(3, Location::RequiresRegister());
1434  locations->SetInAt(4, Location::RequiresRegister());
1435
1436  locations->SetOut(Location::RequiresRegister());
1437}
1438
1439static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorMIPS64* codegen) {
1440  Mips64Assembler* assembler = codegen->GetAssembler();
1441  GpuRegister base = locations->InAt(1).AsRegister<GpuRegister>();
1442  GpuRegister offset = locations->InAt(2).AsRegister<GpuRegister>();
1443  GpuRegister expected = locations->InAt(3).AsRegister<GpuRegister>();
1444  GpuRegister value = locations->InAt(4).AsRegister<GpuRegister>();
1445  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
1446
1447  DCHECK_NE(base, out);
1448  DCHECK_NE(offset, out);
1449  DCHECK_NE(expected, out);
1450
1451  if (type == Primitive::kPrimNot) {
1452    // Mark card for object assuming new value is stored.
1453    bool value_can_be_null = true;  // TODO: Worth finding out this information?
1454    codegen->MarkGCCard(base, value, value_can_be_null);
1455  }
1456
1457  // do {
1458  //   tmp_value = [tmp_ptr] - expected;
1459  // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1460  // result = tmp_value != 0;
1461
1462  Mips64Label loop_head, exit_loop;
1463  __ Daddu(TMP, base, offset);
1464  __ Sync(0);
1465  __ Bind(&loop_head);
1466  if (type == Primitive::kPrimLong) {
1467    __ Lld(out, TMP);
1468  } else {
1469    // Note: We will need a read barrier here, when read barrier
1470    // support is added to the MIPS64 back end.
1471    __ Ll(out, TMP);
1472  }
1473  __ Dsubu(out, out, expected);         // If we didn't get the 'expected'
1474  __ Sltiu(out, out, 1);                // value, set 'out' to false, and
1475  __ Beqzc(out, &exit_loop);            // return.
1476  __ Move(out, value);  // Use 'out' for the 'store conditional' instruction.
1477                        // If we use 'value' directly, we would lose 'value'
1478                        // in the case that the store fails.  Whether the
1479                        // store succeeds, or fails, it will load the
1480                        // correct boolean value into the 'out' register.
1481  if (type == Primitive::kPrimLong) {
1482    __ Scd(out, TMP);
1483  } else {
1484    __ Sc(out, TMP);
1485  }
1486  __ Beqzc(out, &loop_head);    // If we couldn't do the read-modify-write
1487                                // cycle atomically then retry.
1488  __ Bind(&exit_loop);
1489  __ Sync(0);
1490}
1491
1492// boolean sun.misc.Unsafe.compareAndSwapInt(Object o, long offset, int expected, int x)
1493void IntrinsicLocationsBuilderMIPS64::VisitUnsafeCASInt(HInvoke* invoke) {
1494  CreateIntIntIntIntIntToInt(arena_, invoke);
1495}
1496
1497void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASInt(HInvoke* invoke) {
1498  GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
1499}
1500
1501// boolean sun.misc.Unsafe.compareAndSwapLong(Object o, long offset, long expected, long x)
1502void IntrinsicLocationsBuilderMIPS64::VisitUnsafeCASLong(HInvoke* invoke) {
1503  CreateIntIntIntIntIntToInt(arena_, invoke);
1504}
1505
1506void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASLong(HInvoke* invoke) {
1507  GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
1508}
1509
1510// boolean sun.misc.Unsafe.compareAndSwapObject(Object o, long offset, Object expected, Object x)
1511void IntrinsicLocationsBuilderMIPS64::VisitUnsafeCASObject(HInvoke* invoke) {
1512  CreateIntIntIntIntIntToInt(arena_, invoke);
1513}
1514
1515void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASObject(HInvoke* invoke) {
1516  GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
1517}
1518
1519// int java.lang.String.compareTo(String anotherString)
1520void IntrinsicLocationsBuilderMIPS64::VisitStringCompareTo(HInvoke* invoke) {
1521  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1522                                                            LocationSummary::kCallOnMainOnly,
1523                                                            kIntrinsified);
1524  InvokeRuntimeCallingConvention calling_convention;
1525  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1526  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1527  Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt);
1528  locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>()));
1529}
1530
1531void IntrinsicCodeGeneratorMIPS64::VisitStringCompareTo(HInvoke* invoke) {
1532  Mips64Assembler* assembler = GetAssembler();
1533  LocationSummary* locations = invoke->GetLocations();
1534
1535  // Note that the null check must have been done earlier.
1536  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1537
1538  GpuRegister argument = locations->InAt(1).AsRegister<GpuRegister>();
1539  SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke);
1540  codegen_->AddSlowPath(slow_path);
1541  __ Beqzc(argument, slow_path->GetEntryLabel());
1542
1543  __ LoadFromOffset(kLoadDoubleword,
1544                    T9,
1545                    TR,
1546                    QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pStringCompareTo).Int32Value());
1547  __ Jalr(T9);
1548  __ Nop();
1549  __ Bind(slow_path->GetExitLabel());
1550}
1551
1552// boolean java.lang.String.equals(Object anObject)
1553void IntrinsicLocationsBuilderMIPS64::VisitStringEquals(HInvoke* invoke) {
1554  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1555                                                            LocationSummary::kNoCall,
1556                                                            kIntrinsified);
1557  locations->SetInAt(0, Location::RequiresRegister());
1558  locations->SetInAt(1, Location::RequiresRegister());
1559  locations->SetOut(Location::RequiresRegister());
1560
1561  // Temporary registers to store lengths of strings and for calculations.
1562  locations->AddTemp(Location::RequiresRegister());
1563  locations->AddTemp(Location::RequiresRegister());
1564  locations->AddTemp(Location::RequiresRegister());
1565}
1566
1567void IntrinsicCodeGeneratorMIPS64::VisitStringEquals(HInvoke* invoke) {
1568  Mips64Assembler* assembler = GetAssembler();
1569  LocationSummary* locations = invoke->GetLocations();
1570
1571  GpuRegister str = locations->InAt(0).AsRegister<GpuRegister>();
1572  GpuRegister arg = locations->InAt(1).AsRegister<GpuRegister>();
1573  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
1574
1575  GpuRegister temp1 = locations->GetTemp(0).AsRegister<GpuRegister>();
1576  GpuRegister temp2 = locations->GetTemp(1).AsRegister<GpuRegister>();
1577  GpuRegister temp3 = locations->GetTemp(2).AsRegister<GpuRegister>();
1578
1579  Mips64Label loop;
1580  Mips64Label end;
1581  Mips64Label return_true;
1582  Mips64Label return_false;
1583
1584  // Get offsets of count, value, and class fields within a string object.
1585  const int32_t count_offset = mirror::String::CountOffset().Int32Value();
1586  const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1587  const int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1588
1589  // Note that the null check must have been done earlier.
1590  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1591
1592  // If the register containing the pointer to "this", and the register
1593  // containing the pointer to "anObject" are the same register then
1594  // "this", and "anObject" are the same object and we can
1595  // short-circuit the logic to a true result.
1596  if (str == arg) {
1597    __ LoadConst64(out, 1);
1598    return;
1599  }
1600
1601  // Check if input is null, return false if it is.
1602  __ Beqzc(arg, &return_false);
1603
1604  // Reference equality check, return true if same reference.
1605  __ Beqc(str, arg, &return_true);
1606
1607  // Instanceof check for the argument by comparing class fields.
1608  // All string objects must have the same type since String cannot be subclassed.
1609  // Receiver must be a string object, so its class field is equal to all strings' class fields.
1610  // If the argument is a string object, its class field must be equal to receiver's class field.
1611  __ Lw(temp1, str, class_offset);
1612  __ Lw(temp2, arg, class_offset);
1613  __ Bnec(temp1, temp2, &return_false);
1614
1615  // Load lengths of this and argument strings.
1616  __ Lw(temp1, str, count_offset);
1617  __ Lw(temp2, arg, count_offset);
1618  // Check if lengths are equal, return false if they're not.
1619  __ Bnec(temp1, temp2, &return_false);
1620  // Return true if both strings are empty.
1621  __ Beqzc(temp1, &return_true);
1622
1623  // Don't overwrite input registers
1624  __ Move(TMP, str);
1625  __ Move(temp3, arg);
1626
1627  // Assertions that must hold in order to compare strings 4 characters at a time.
1628  DCHECK_ALIGNED(value_offset, 8);
1629  static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
1630
1631  // Loop to compare strings 4 characters at a time starting at the beginning of the string.
1632  // Ok to do this because strings are zero-padded to be 8-byte aligned.
1633  __ Bind(&loop);
1634  __ Ld(out, TMP, value_offset);
1635  __ Ld(temp2, temp3, value_offset);
1636  __ Bnec(out, temp2, &return_false);
1637  __ Daddiu(TMP, TMP, 8);
1638  __ Daddiu(temp3, temp3, 8);
1639  __ Addiu(temp1, temp1, -4);
1640  __ Bgtzc(temp1, &loop);
1641
1642  // Return true and exit the function.
1643  // If loop does not result in returning false, we return true.
1644  __ Bind(&return_true);
1645  __ LoadConst64(out, 1);
1646  __ Bc(&end);
1647
1648  // Return false and exit the function.
1649  __ Bind(&return_false);
1650  __ LoadConst64(out, 0);
1651  __ Bind(&end);
1652}
1653
1654static void GenerateStringIndexOf(HInvoke* invoke,
1655                                  Mips64Assembler* assembler,
1656                                  CodeGeneratorMIPS64* codegen,
1657                                  ArenaAllocator* allocator,
1658                                  bool start_at_zero) {
1659  LocationSummary* locations = invoke->GetLocations();
1660  GpuRegister tmp_reg = start_at_zero ? locations->GetTemp(0).AsRegister<GpuRegister>() : TMP;
1661
1662  // Note that the null check must have been done earlier.
1663  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1664
1665  // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
1666  // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
1667  SlowPathCodeMIPS64* slow_path = nullptr;
1668  HInstruction* code_point = invoke->InputAt(1);
1669  if (code_point->IsIntConstant()) {
1670    if (!IsUint<16>(code_point->AsIntConstant()->GetValue())) {
1671      // Always needs the slow-path. We could directly dispatch to it,
1672      // but this case should be rare, so for simplicity just put the
1673      // full slow-path down and branch unconditionally.
1674      slow_path = new (allocator) IntrinsicSlowPathMIPS64(invoke);
1675      codegen->AddSlowPath(slow_path);
1676      __ Bc(slow_path->GetEntryLabel());
1677      __ Bind(slow_path->GetExitLabel());
1678      return;
1679    }
1680  } else if (code_point->GetType() != Primitive::kPrimChar) {
1681    GpuRegister char_reg = locations->InAt(1).AsRegister<GpuRegister>();
1682    __ LoadConst32(tmp_reg, std::numeric_limits<uint16_t>::max());
1683    slow_path = new (allocator) IntrinsicSlowPathMIPS64(invoke);
1684    codegen->AddSlowPath(slow_path);
1685    __ Bltuc(tmp_reg, char_reg, slow_path->GetEntryLabel());    // UTF-16 required
1686  }
1687
1688  if (start_at_zero) {
1689    DCHECK_EQ(tmp_reg, A2);
1690    // Start-index = 0.
1691    __ Clear(tmp_reg);
1692  }
1693
1694  __ LoadFromOffset(kLoadDoubleword,
1695                    T9,
1696                    TR,
1697                    QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pIndexOf).Int32Value());
1698  CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
1699  __ Jalr(T9);
1700  __ Nop();
1701
1702  if (slow_path != nullptr) {
1703    __ Bind(slow_path->GetExitLabel());
1704  }
1705}
1706
1707// int java.lang.String.indexOf(int ch)
1708void IntrinsicLocationsBuilderMIPS64::VisitStringIndexOf(HInvoke* invoke) {
1709  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1710                                                            LocationSummary::kCallOnMainAndSlowPath,
1711                                                            kIntrinsified);
1712  // We have a hand-crafted assembly stub that follows the runtime
1713  // calling convention. So it's best to align the inputs accordingly.
1714  InvokeRuntimeCallingConvention calling_convention;
1715  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1716  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1717  Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt);
1718  locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>()));
1719
1720  // Need a temp for slow-path codepoint compare, and need to send start-index=0.
1721  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1722}
1723
1724void IntrinsicCodeGeneratorMIPS64::VisitStringIndexOf(HInvoke* invoke) {
1725  GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
1726}
1727
1728// int java.lang.String.indexOf(int ch, int fromIndex)
1729void IntrinsicLocationsBuilderMIPS64::VisitStringIndexOfAfter(HInvoke* invoke) {
1730  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1731                                                            LocationSummary::kCallOnMainAndSlowPath,
1732                                                            kIntrinsified);
1733  // We have a hand-crafted assembly stub that follows the runtime
1734  // calling convention. So it's best to align the inputs accordingly.
1735  InvokeRuntimeCallingConvention calling_convention;
1736  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1737  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1738  locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1739  Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt);
1740  locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>()));
1741}
1742
1743void IntrinsicCodeGeneratorMIPS64::VisitStringIndexOfAfter(HInvoke* invoke) {
1744  GenerateStringIndexOf(
1745      invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
1746}
1747
1748// java.lang.StringFactory.newStringFromBytes(byte[] data, int high, int offset, int byteCount)
1749void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1750  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1751                                                            LocationSummary::kCallOnMainAndSlowPath,
1752                                                            kIntrinsified);
1753  InvokeRuntimeCallingConvention calling_convention;
1754  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1755  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1756  locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1757  locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
1758  Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt);
1759  locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>()));
1760}
1761
1762void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke) {
1763  Mips64Assembler* assembler = GetAssembler();
1764  LocationSummary* locations = invoke->GetLocations();
1765
1766  GpuRegister byte_array = locations->InAt(0).AsRegister<GpuRegister>();
1767  SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke);
1768  codegen_->AddSlowPath(slow_path);
1769  __ Beqzc(byte_array, slow_path->GetEntryLabel());
1770
1771  __ LoadFromOffset(kLoadDoubleword,
1772                    T9,
1773                    TR,
1774                    QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize,
1775                                            pAllocStringFromBytes).Int32Value());
1776  CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
1777  __ Jalr(T9);
1778  __ Nop();
1779  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1780  __ Bind(slow_path->GetExitLabel());
1781}
1782
1783// java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1784void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromChars(HInvoke* invoke) {
1785  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1786                                                            LocationSummary::kCallOnMainOnly,
1787                                                            kIntrinsified);
1788  InvokeRuntimeCallingConvention calling_convention;
1789  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1790  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1791  locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1792  Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt);
1793  locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>()));
1794}
1795
1796void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromChars(HInvoke* invoke) {
1797  Mips64Assembler* assembler = GetAssembler();
1798
1799  // No need to emit code checking whether `locations->InAt(2)` is a null
1800  // pointer, as callers of the native method
1801  //
1802  //   java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1803  //
1804  // all include a null check on `data` before calling that method.
1805  __ LoadFromOffset(kLoadDoubleword,
1806                    T9,
1807                    TR,
1808                    QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize,
1809                                            pAllocStringFromChars).Int32Value());
1810  CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
1811  __ Jalr(T9);
1812  __ Nop();
1813  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1814}
1815
1816// java.lang.StringFactory.newStringFromString(String toCopy)
1817void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromString(HInvoke* invoke) {
1818  LocationSummary* locations = new (arena_) LocationSummary(invoke,
1819                                                            LocationSummary::kCallOnMainAndSlowPath,
1820                                                            kIntrinsified);
1821  InvokeRuntimeCallingConvention calling_convention;
1822  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1823  Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt);
1824  locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>()));
1825}
1826
1827void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromString(HInvoke* invoke) {
1828  Mips64Assembler* assembler = GetAssembler();
1829  LocationSummary* locations = invoke->GetLocations();
1830
1831  GpuRegister string_to_copy = locations->InAt(0).AsRegister<GpuRegister>();
1832  SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke);
1833  codegen_->AddSlowPath(slow_path);
1834  __ Beqzc(string_to_copy, slow_path->GetEntryLabel());
1835
1836  __ LoadFromOffset(kLoadDoubleword,
1837                    T9,
1838                    TR,
1839                    QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize,
1840                                            pAllocStringFromString).Int32Value());
1841  CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
1842  __ Jalr(T9);
1843  __ Nop();
1844  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1845  __ Bind(slow_path->GetExitLabel());
1846}
1847
1848static void GenIsInfinite(LocationSummary* locations,
1849                          bool is64bit,
1850                          Mips64Assembler* assembler) {
1851  FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
1852  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
1853
1854  if (is64bit) {
1855    __ ClassD(FTMP, in);
1856  } else {
1857    __ ClassS(FTMP, in);
1858  }
1859  __ Mfc1(out, FTMP);
1860  __ Andi(out, out, kPositiveInfinity | kNegativeInfinity);
1861  __ Sltu(out, ZERO, out);
1862}
1863
1864// boolean java.lang.Float.isInfinite(float)
1865void IntrinsicLocationsBuilderMIPS64::VisitFloatIsInfinite(HInvoke* invoke) {
1866  CreateFPToIntLocations(arena_, invoke);
1867}
1868
1869void IntrinsicCodeGeneratorMIPS64::VisitFloatIsInfinite(HInvoke* invoke) {
1870  GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
1871}
1872
1873// boolean java.lang.Double.isInfinite(double)
1874void IntrinsicLocationsBuilderMIPS64::VisitDoubleIsInfinite(HInvoke* invoke) {
1875  CreateFPToIntLocations(arena_, invoke);
1876}
1877
1878void IntrinsicCodeGeneratorMIPS64::VisitDoubleIsInfinite(HInvoke* invoke) {
1879  GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
1880}
1881
1882static void GenHighestOneBit(LocationSummary* locations,
1883                             Primitive::Type type,
1884                             Mips64Assembler* assembler) {
1885  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong) << PrettyDescriptor(type);
1886
1887  GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>();
1888  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
1889
1890  if (type == Primitive::kPrimLong) {
1891    __ Dclz(TMP, in);
1892    __ LoadConst64(AT, INT64_C(0x8000000000000000));
1893    __ Dsrlv(out, AT, TMP);
1894  } else {
1895    __ Clz(TMP, in);
1896    __ LoadConst32(AT, 0x80000000);
1897    __ Srlv(out, AT, TMP);
1898  }
1899  // For either value of "type", when "in" is zero, "out" should also
1900  // be zero. Without this extra "and" operation, when "in" is zero,
1901  // "out" would be either Integer.MIN_VALUE, or Long.MIN_VALUE because
1902  // the MIPS logical shift operations "dsrlv", and "srlv" don't use
1903  // the shift amount (TMP) directly; they use either (TMP % 64) or
1904  // (TMP % 32), respectively.
1905  __ And(out, out, in);
1906}
1907
1908// int java.lang.Integer.highestOneBit(int)
1909void IntrinsicLocationsBuilderMIPS64::VisitIntegerHighestOneBit(HInvoke* invoke) {
1910  CreateIntToIntLocations(arena_, invoke);
1911}
1912
1913void IntrinsicCodeGeneratorMIPS64::VisitIntegerHighestOneBit(HInvoke* invoke) {
1914  GenHighestOneBit(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
1915}
1916
1917// long java.lang.Long.highestOneBit(long)
1918void IntrinsicLocationsBuilderMIPS64::VisitLongHighestOneBit(HInvoke* invoke) {
1919  CreateIntToIntLocations(arena_, invoke);
1920}
1921
1922void IntrinsicCodeGeneratorMIPS64::VisitLongHighestOneBit(HInvoke* invoke) {
1923  GenHighestOneBit(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
1924}
1925
1926static void GenLowestOneBit(LocationSummary* locations,
1927                            Primitive::Type type,
1928                            Mips64Assembler* assembler) {
1929  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong) << PrettyDescriptor(type);
1930
1931  GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>();
1932  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
1933
1934  if (type == Primitive::kPrimLong) {
1935    __ Dsubu(TMP, ZERO, in);
1936  } else {
1937    __ Subu(TMP, ZERO, in);
1938  }
1939  __ And(out, TMP, in);
1940}
1941
1942// int java.lang.Integer.lowestOneBit(int)
1943void IntrinsicLocationsBuilderMIPS64::VisitIntegerLowestOneBit(HInvoke* invoke) {
1944  CreateIntToIntLocations(arena_, invoke);
1945}
1946
1947void IntrinsicCodeGeneratorMIPS64::VisitIntegerLowestOneBit(HInvoke* invoke) {
1948  GenLowestOneBit(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
1949}
1950
1951// long java.lang.Long.lowestOneBit(long)
1952void IntrinsicLocationsBuilderMIPS64::VisitLongLowestOneBit(HInvoke* invoke) {
1953  CreateIntToIntLocations(arena_, invoke);
1954}
1955
1956void IntrinsicCodeGeneratorMIPS64::VisitLongLowestOneBit(HInvoke* invoke) {
1957  GenLowestOneBit(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
1958}
1959
1960UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
1961UNIMPLEMENTED_INTRINSIC(MIPS64, StringGetCharsNoCheck)
1962UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopyChar)
1963UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
1964
1965UNIMPLEMENTED_INTRINSIC(MIPS64, MathCos)
1966UNIMPLEMENTED_INTRINSIC(MIPS64, MathSin)
1967UNIMPLEMENTED_INTRINSIC(MIPS64, MathAcos)
1968UNIMPLEMENTED_INTRINSIC(MIPS64, MathAsin)
1969UNIMPLEMENTED_INTRINSIC(MIPS64, MathAtan)
1970UNIMPLEMENTED_INTRINSIC(MIPS64, MathAtan2)
1971UNIMPLEMENTED_INTRINSIC(MIPS64, MathCbrt)
1972UNIMPLEMENTED_INTRINSIC(MIPS64, MathCosh)
1973UNIMPLEMENTED_INTRINSIC(MIPS64, MathExp)
1974UNIMPLEMENTED_INTRINSIC(MIPS64, MathExpm1)
1975UNIMPLEMENTED_INTRINSIC(MIPS64, MathHypot)
1976UNIMPLEMENTED_INTRINSIC(MIPS64, MathLog)
1977UNIMPLEMENTED_INTRINSIC(MIPS64, MathLog10)
1978UNIMPLEMENTED_INTRINSIC(MIPS64, MathNextAfter)
1979UNIMPLEMENTED_INTRINSIC(MIPS64, MathSinh)
1980UNIMPLEMENTED_INTRINSIC(MIPS64, MathTan)
1981UNIMPLEMENTED_INTRINSIC(MIPS64, MathTanh)
1982
1983// 1.8.
1984UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddInt)
1985UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddLong)
1986UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetInt)
1987UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetLong)
1988UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetObject)
1989
1990UNREACHABLE_INTRINSICS(MIPS64)
1991
1992#undef __
1993
1994}  // namespace mips64
1995}  // namespace art
1996