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