intrinsics_arm64.cc revision 2e50ecb95ddf645595491438cf35e79b705ee366
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_arm64.h" 18 19#include "arch/arm64/instruction_set_features_arm64.h" 20#include "art_method.h" 21#include "code_generator_arm64.h" 22#include "common_arm64.h" 23#include "entrypoints/quick/quick_entrypoints.h" 24#include "intrinsics.h" 25#include "mirror/array-inl.h" 26#include "mirror/string.h" 27#include "thread.h" 28#include "utils/arm64/assembler_arm64.h" 29#include "utils/arm64/constants_arm64.h" 30 31#include "vixl/a64/disasm-a64.h" 32#include "vixl/a64/macro-assembler-a64.h" 33 34using namespace vixl; // NOLINT(build/namespaces) 35 36namespace art { 37 38namespace arm64 { 39 40using helpers::DRegisterFrom; 41using helpers::FPRegisterFrom; 42using helpers::HeapOperand; 43using helpers::LocationFrom; 44using helpers::OperandFrom; 45using helpers::RegisterFrom; 46using helpers::SRegisterFrom; 47using helpers::WRegisterFrom; 48using helpers::XRegisterFrom; 49 50namespace { 51 52ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) { 53 return MemOperand(XRegisterFrom(location), offset); 54} 55 56} // namespace 57 58vixl::MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() { 59 return codegen_->GetAssembler()->vixl_masm_; 60} 61 62ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() { 63 return codegen_->GetGraph()->GetArena(); 64} 65 66#define __ codegen->GetAssembler()->vixl_masm_-> 67 68static void MoveFromReturnRegister(Location trg, 69 Primitive::Type type, 70 CodeGeneratorARM64* codegen) { 71 if (!trg.IsValid()) { 72 DCHECK(type == Primitive::kPrimVoid); 73 return; 74 } 75 76 DCHECK_NE(type, Primitive::kPrimVoid); 77 78 if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) { 79 Register trg_reg = RegisterFrom(trg, type); 80 Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type); 81 __ Mov(trg_reg, res_reg, kDiscardForSameWReg); 82 } else { 83 FPRegister trg_reg = FPRegisterFrom(trg, type); 84 FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type); 85 __ Fmov(trg_reg, res_reg); 86 } 87} 88 89static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) { 90 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor; 91 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor); 92} 93 94// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified 95// call. This will copy the arguments into the positions for a regular call. 96// 97// Note: The actual parameters are required to be in the locations given by the invoke's location 98// summary. If an intrinsic modifies those locations before a slowpath call, they must be 99// restored! 100class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 { 101 public: 102 explicit IntrinsicSlowPathARM64(HInvoke* invoke) : invoke_(invoke) { } 103 104 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { 105 CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in); 106 __ Bind(GetEntryLabel()); 107 108 SaveLiveRegisters(codegen, invoke_->GetLocations()); 109 110 MoveArguments(invoke_, codegen); 111 112 if (invoke_->IsInvokeStaticOrDirect()) { 113 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), 114 LocationFrom(kArtMethodRegister)); 115 } else { 116 codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister)); 117 } 118 codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); 119 120 // Copy the result back to the expected output. 121 Location out = invoke_->GetLocations()->Out(); 122 if (out.IsValid()) { 123 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory. 124 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg())); 125 MoveFromReturnRegister(out, invoke_->GetType(), codegen); 126 } 127 128 RestoreLiveRegisters(codegen, invoke_->GetLocations()); 129 __ B(GetExitLabel()); 130 } 131 132 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; } 133 134 private: 135 // The instruction where this slow path is happening. 136 HInvoke* const invoke_; 137 138 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64); 139}; 140 141#undef __ 142 143bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) { 144 Dispatch(invoke); 145 LocationSummary* res = invoke->GetLocations(); 146 if (res == nullptr) { 147 return false; 148 } 149 if (kEmitCompilerReadBarrier && res->CanCall()) { 150 // Generating an intrinsic for this HInvoke may produce an 151 // IntrinsicSlowPathARM64 slow path. Currently this approach 152 // does not work when using read barriers, as the emitted 153 // calling sequence will make use of another slow path 154 // (ReadBarrierForRootSlowPathARM64 for HInvokeStaticOrDirect, 155 // ReadBarrierSlowPathARM64 for HInvokeVirtual). So we bail 156 // out in this case. 157 // 158 // TODO: Find a way to have intrinsics work with read barriers. 159 invoke->SetLocations(nullptr); 160 return false; 161 } 162 return res->Intrinsified(); 163} 164 165#define __ masm-> 166 167static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { 168 LocationSummary* locations = new (arena) LocationSummary(invoke, 169 LocationSummary::kNoCall, 170 kIntrinsified); 171 locations->SetInAt(0, Location::RequiresFpuRegister()); 172 locations->SetOut(Location::RequiresRegister()); 173} 174 175static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { 176 LocationSummary* locations = new (arena) LocationSummary(invoke, 177 LocationSummary::kNoCall, 178 kIntrinsified); 179 locations->SetInAt(0, Location::RequiresRegister()); 180 locations->SetOut(Location::RequiresFpuRegister()); 181} 182 183static void MoveFPToInt(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) { 184 Location input = locations->InAt(0); 185 Location output = locations->Out(); 186 __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output), 187 is64bit ? DRegisterFrom(input) : SRegisterFrom(input)); 188} 189 190static void MoveIntToFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) { 191 Location input = locations->InAt(0); 192 Location output = locations->Out(); 193 __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output), 194 is64bit ? XRegisterFrom(input) : WRegisterFrom(input)); 195} 196 197void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) { 198 CreateFPToIntLocations(arena_, invoke); 199} 200void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) { 201 CreateIntToFPLocations(arena_, invoke); 202} 203 204void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) { 205 MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler()); 206} 207void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) { 208 MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler()); 209} 210 211void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) { 212 CreateFPToIntLocations(arena_, invoke); 213} 214void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) { 215 CreateIntToFPLocations(arena_, invoke); 216} 217 218void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) { 219 MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler()); 220} 221void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) { 222 MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler()); 223} 224 225static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { 226 LocationSummary* locations = new (arena) LocationSummary(invoke, 227 LocationSummary::kNoCall, 228 kIntrinsified); 229 locations->SetInAt(0, Location::RequiresRegister()); 230 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 231} 232 233static void GenReverseBytes(LocationSummary* locations, 234 Primitive::Type type, 235 vixl::MacroAssembler* masm) { 236 Location in = locations->InAt(0); 237 Location out = locations->Out(); 238 239 switch (type) { 240 case Primitive::kPrimShort: 241 __ Rev16(WRegisterFrom(out), WRegisterFrom(in)); 242 __ Sxth(WRegisterFrom(out), WRegisterFrom(out)); 243 break; 244 case Primitive::kPrimInt: 245 case Primitive::kPrimLong: 246 __ Rev(RegisterFrom(out, type), RegisterFrom(in, type)); 247 break; 248 default: 249 LOG(FATAL) << "Unexpected size for reverse-bytes: " << type; 250 UNREACHABLE(); 251 } 252} 253 254void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) { 255 CreateIntToIntLocations(arena_, invoke); 256} 257 258void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) { 259 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler()); 260} 261 262void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) { 263 CreateIntToIntLocations(arena_, invoke); 264} 265 266void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) { 267 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler()); 268} 269 270void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) { 271 CreateIntToIntLocations(arena_, invoke); 272} 273 274void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) { 275 GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler()); 276} 277 278static void GenNumberOfLeadingZeros(LocationSummary* locations, 279 Primitive::Type type, 280 vixl::MacroAssembler* masm) { 281 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); 282 283 Location in = locations->InAt(0); 284 Location out = locations->Out(); 285 286 __ Clz(RegisterFrom(out, type), RegisterFrom(in, type)); 287} 288 289void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { 290 CreateIntToIntLocations(arena_, invoke); 291} 292 293void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { 294 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler()); 295} 296 297void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { 298 CreateIntToIntLocations(arena_, invoke); 299} 300 301void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { 302 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler()); 303} 304 305static void GenNumberOfTrailingZeros(LocationSummary* locations, 306 Primitive::Type type, 307 vixl::MacroAssembler* masm) { 308 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); 309 310 Location in = locations->InAt(0); 311 Location out = locations->Out(); 312 313 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type)); 314 __ Clz(RegisterFrom(out, type), RegisterFrom(out, type)); 315} 316 317void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { 318 CreateIntToIntLocations(arena_, invoke); 319} 320 321void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { 322 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler()); 323} 324 325void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { 326 CreateIntToIntLocations(arena_, invoke); 327} 328 329void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { 330 GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler()); 331} 332 333static void GenReverse(LocationSummary* locations, 334 Primitive::Type type, 335 vixl::MacroAssembler* masm) { 336 DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); 337 338 Location in = locations->InAt(0); 339 Location out = locations->Out(); 340 341 __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type)); 342} 343 344void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) { 345 CreateIntToIntLocations(arena_, invoke); 346} 347 348void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) { 349 GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler()); 350} 351 352void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) { 353 CreateIntToIntLocations(arena_, invoke); 354} 355 356void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) { 357 GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler()); 358} 359 360static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { 361 LocationSummary* locations = new (arena) LocationSummary(invoke, 362 LocationSummary::kNoCall, 363 kIntrinsified); 364 locations->SetInAt(0, Location::RequiresFpuRegister()); 365 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 366} 367 368static void MathAbsFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) { 369 Location in = locations->InAt(0); 370 Location out = locations->Out(); 371 372 FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in); 373 FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out); 374 375 __ Fabs(out_reg, in_reg); 376} 377 378void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) { 379 CreateFPToFPLocations(arena_, invoke); 380} 381 382void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) { 383 MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler()); 384} 385 386void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) { 387 CreateFPToFPLocations(arena_, invoke); 388} 389 390void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) { 391 MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler()); 392} 393 394static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) { 395 LocationSummary* locations = new (arena) LocationSummary(invoke, 396 LocationSummary::kNoCall, 397 kIntrinsified); 398 locations->SetInAt(0, Location::RequiresRegister()); 399 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 400} 401 402static void GenAbsInteger(LocationSummary* locations, 403 bool is64bit, 404 vixl::MacroAssembler* masm) { 405 Location in = locations->InAt(0); 406 Location output = locations->Out(); 407 408 Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in); 409 Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output); 410 411 __ Cmp(in_reg, Operand(0)); 412 __ Cneg(out_reg, in_reg, lt); 413} 414 415void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) { 416 CreateIntToInt(arena_, invoke); 417} 418 419void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) { 420 GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler()); 421} 422 423void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) { 424 CreateIntToInt(arena_, invoke); 425} 426 427void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) { 428 GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler()); 429} 430 431static void GenMinMaxFP(LocationSummary* locations, 432 bool is_min, 433 bool is_double, 434 vixl::MacroAssembler* masm) { 435 Location op1 = locations->InAt(0); 436 Location op2 = locations->InAt(1); 437 Location out = locations->Out(); 438 439 FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1); 440 FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2); 441 FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out); 442 if (is_min) { 443 __ Fmin(out_reg, op1_reg, op2_reg); 444 } else { 445 __ Fmax(out_reg, op1_reg, op2_reg); 446 } 447} 448 449static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { 450 LocationSummary* locations = new (arena) LocationSummary(invoke, 451 LocationSummary::kNoCall, 452 kIntrinsified); 453 locations->SetInAt(0, Location::RequiresFpuRegister()); 454 locations->SetInAt(1, Location::RequiresFpuRegister()); 455 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 456} 457 458void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) { 459 CreateFPFPToFPLocations(arena_, invoke); 460} 461 462void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) { 463 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler()); 464} 465 466void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) { 467 CreateFPFPToFPLocations(arena_, invoke); 468} 469 470void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) { 471 GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler()); 472} 473 474void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) { 475 CreateFPFPToFPLocations(arena_, invoke); 476} 477 478void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) { 479 GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler()); 480} 481 482void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) { 483 CreateFPFPToFPLocations(arena_, invoke); 484} 485 486void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) { 487 GenMinMaxFP( 488 invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler()); 489} 490 491static void GenMinMax(LocationSummary* locations, 492 bool is_min, 493 bool is_long, 494 vixl::MacroAssembler* masm) { 495 Location op1 = locations->InAt(0); 496 Location op2 = locations->InAt(1); 497 Location out = locations->Out(); 498 499 Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1); 500 Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2); 501 Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out); 502 503 __ Cmp(op1_reg, op2_reg); 504 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt); 505} 506 507static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { 508 LocationSummary* locations = new (arena) LocationSummary(invoke, 509 LocationSummary::kNoCall, 510 kIntrinsified); 511 locations->SetInAt(0, Location::RequiresRegister()); 512 locations->SetInAt(1, Location::RequiresRegister()); 513 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 514} 515 516void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) { 517 CreateIntIntToIntLocations(arena_, invoke); 518} 519 520void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) { 521 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler()); 522} 523 524void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) { 525 CreateIntIntToIntLocations(arena_, invoke); 526} 527 528void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) { 529 GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler()); 530} 531 532void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) { 533 CreateIntIntToIntLocations(arena_, invoke); 534} 535 536void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) { 537 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler()); 538} 539 540void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) { 541 CreateIntIntToIntLocations(arena_, invoke); 542} 543 544void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) { 545 GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler()); 546} 547 548void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) { 549 CreateFPToFPLocations(arena_, invoke); 550} 551 552void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) { 553 LocationSummary* locations = invoke->GetLocations(); 554 vixl::MacroAssembler* masm = GetVIXLAssembler(); 555 __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0))); 556} 557 558void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) { 559 CreateFPToFPLocations(arena_, invoke); 560} 561 562void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) { 563 LocationSummary* locations = invoke->GetLocations(); 564 vixl::MacroAssembler* masm = GetVIXLAssembler(); 565 __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0))); 566} 567 568void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) { 569 CreateFPToFPLocations(arena_, invoke); 570} 571 572void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) { 573 LocationSummary* locations = invoke->GetLocations(); 574 vixl::MacroAssembler* masm = GetVIXLAssembler(); 575 __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0))); 576} 577 578void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) { 579 CreateFPToFPLocations(arena_, invoke); 580} 581 582void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) { 583 LocationSummary* locations = invoke->GetLocations(); 584 vixl::MacroAssembler* masm = GetVIXLAssembler(); 585 __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0))); 586} 587 588static void CreateFPToIntPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) { 589 LocationSummary* locations = new (arena) LocationSummary(invoke, 590 LocationSummary::kNoCall, 591 kIntrinsified); 592 locations->SetInAt(0, Location::RequiresFpuRegister()); 593 locations->SetOut(Location::RequiresRegister()); 594} 595 596static void GenMathRound(LocationSummary* locations, 597 bool is_double, 598 vixl::MacroAssembler* masm) { 599 FPRegister in_reg = is_double ? 600 DRegisterFrom(locations->InAt(0)) : SRegisterFrom(locations->InAt(0)); 601 Register out_reg = is_double ? 602 XRegisterFrom(locations->Out()) : WRegisterFrom(locations->Out()); 603 UseScratchRegisterScope temps(masm); 604 FPRegister temp1_reg = temps.AcquireSameSizeAs(in_reg); 605 606 // 0.5 can be encoded as an immediate, so use fmov. 607 if (is_double) { 608 __ Fmov(temp1_reg, static_cast<double>(0.5)); 609 } else { 610 __ Fmov(temp1_reg, static_cast<float>(0.5)); 611 } 612 __ Fadd(temp1_reg, in_reg, temp1_reg); 613 __ Fcvtms(out_reg, temp1_reg); 614} 615 616void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) { 617 // See intrinsics.h. 618 if (kRoundIsPlusPointFive) { 619 CreateFPToIntPlusTempLocations(arena_, invoke); 620 } 621} 622 623void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) { 624 GenMathRound(invoke->GetLocations(), /* is_double */ true, GetVIXLAssembler()); 625} 626 627void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) { 628 // See intrinsics.h. 629 if (kRoundIsPlusPointFive) { 630 CreateFPToIntPlusTempLocations(arena_, invoke); 631 } 632} 633 634void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) { 635 GenMathRound(invoke->GetLocations(), /* is_double */ false, GetVIXLAssembler()); 636} 637 638void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) { 639 CreateIntToIntLocations(arena_, invoke); 640} 641 642void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) { 643 vixl::MacroAssembler* masm = GetVIXLAssembler(); 644 __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()), 645 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0)); 646} 647 648void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) { 649 CreateIntToIntLocations(arena_, invoke); 650} 651 652void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) { 653 vixl::MacroAssembler* masm = GetVIXLAssembler(); 654 __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()), 655 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0)); 656} 657 658void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) { 659 CreateIntToIntLocations(arena_, invoke); 660} 661 662void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) { 663 vixl::MacroAssembler* masm = GetVIXLAssembler(); 664 __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()), 665 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0)); 666} 667 668void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) { 669 CreateIntToIntLocations(arena_, invoke); 670} 671 672void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) { 673 vixl::MacroAssembler* masm = GetVIXLAssembler(); 674 __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()), 675 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0)); 676} 677 678static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) { 679 LocationSummary* locations = new (arena) LocationSummary(invoke, 680 LocationSummary::kNoCall, 681 kIntrinsified); 682 locations->SetInAt(0, Location::RequiresRegister()); 683 locations->SetInAt(1, Location::RequiresRegister()); 684} 685 686void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) { 687 CreateIntIntToVoidLocations(arena_, invoke); 688} 689 690void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) { 691 vixl::MacroAssembler* masm = GetVIXLAssembler(); 692 __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)), 693 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0)); 694} 695 696void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) { 697 CreateIntIntToVoidLocations(arena_, invoke); 698} 699 700void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) { 701 vixl::MacroAssembler* masm = GetVIXLAssembler(); 702 __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)), 703 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0)); 704} 705 706void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) { 707 CreateIntIntToVoidLocations(arena_, invoke); 708} 709 710void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) { 711 vixl::MacroAssembler* masm = GetVIXLAssembler(); 712 __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)), 713 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0)); 714} 715 716void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) { 717 CreateIntIntToVoidLocations(arena_, invoke); 718} 719 720void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) { 721 vixl::MacroAssembler* masm = GetVIXLAssembler(); 722 __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)), 723 AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0)); 724} 725 726void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) { 727 LocationSummary* locations = new (arena_) LocationSummary(invoke, 728 LocationSummary::kNoCall, 729 kIntrinsified); 730 locations->SetOut(Location::RequiresRegister()); 731} 732 733void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) { 734 codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()), 735 MemOperand(tr, Thread::PeerOffset<8>().Int32Value())); 736} 737 738static void GenUnsafeGet(HInvoke* invoke, 739 Primitive::Type type, 740 bool is_volatile, 741 CodeGeneratorARM64* codegen) { 742 LocationSummary* locations = invoke->GetLocations(); 743 DCHECK((type == Primitive::kPrimInt) || 744 (type == Primitive::kPrimLong) || 745 (type == Primitive::kPrimNot)); 746 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_; 747 Location base_loc = locations->InAt(1); 748 Register base = WRegisterFrom(base_loc); // Object pointer. 749 Location offset_loc = locations->InAt(2); 750 Register offset = XRegisterFrom(offset_loc); // Long offset. 751 Location trg_loc = locations->Out(); 752 Register trg = RegisterFrom(trg_loc, type); 753 bool use_acquire_release = codegen->GetInstructionSetFeatures().PreferAcquireRelease(); 754 755 if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { 756 // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case. 757 UseScratchRegisterScope temps(masm); 758 Register temp = temps.AcquireW(); 759 codegen->GenerateArrayLoadWithBakerReadBarrier( 760 invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); 761 if (is_volatile && !use_acquire_release) { 762 __ Dmb(InnerShareable, BarrierReads); 763 } 764 } else { 765 // Other cases. 766 MemOperand mem_op(base.X(), offset); 767 if (is_volatile) { 768 if (use_acquire_release) { 769 codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true); 770 } else { 771 codegen->Load(type, trg, mem_op); 772 __ Dmb(InnerShareable, BarrierReads); 773 } 774 } else { 775 codegen->Load(type, trg, mem_op); 776 } 777 778 if (type == Primitive::kPrimNot) { 779 DCHECK(trg.IsW()); 780 codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc); 781 } 782 } 783} 784 785static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { 786 bool can_call = kEmitCompilerReadBarrier && 787 (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject || 788 invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile); 789 LocationSummary* locations = new (arena) LocationSummary(invoke, 790 can_call ? 791 LocationSummary::kCallOnSlowPath : 792 LocationSummary::kNoCall, 793 kIntrinsified); 794 locations->SetInAt(0, Location::NoLocation()); // Unused receiver. 795 locations->SetInAt(1, Location::RequiresRegister()); 796 locations->SetInAt(2, Location::RequiresRegister()); 797 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 798} 799 800void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) { 801 CreateIntIntIntToIntLocations(arena_, invoke); 802} 803void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) { 804 CreateIntIntIntToIntLocations(arena_, invoke); 805} 806void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) { 807 CreateIntIntIntToIntLocations(arena_, invoke); 808} 809void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) { 810 CreateIntIntIntToIntLocations(arena_, invoke); 811} 812void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) { 813 CreateIntIntIntToIntLocations(arena_, invoke); 814} 815void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { 816 CreateIntIntIntToIntLocations(arena_, invoke); 817} 818 819void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) { 820 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_); 821} 822void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) { 823 GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_); 824} 825void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) { 826 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_); 827} 828void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) { 829 GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_); 830} 831void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) { 832 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_); 833} 834void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { 835 GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_); 836} 837 838static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) { 839 LocationSummary* locations = new (arena) LocationSummary(invoke, 840 LocationSummary::kNoCall, 841 kIntrinsified); 842 locations->SetInAt(0, Location::NoLocation()); // Unused receiver. 843 locations->SetInAt(1, Location::RequiresRegister()); 844 locations->SetInAt(2, Location::RequiresRegister()); 845 locations->SetInAt(3, Location::RequiresRegister()); 846} 847 848void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) { 849 CreateIntIntIntIntToVoid(arena_, invoke); 850} 851void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) { 852 CreateIntIntIntIntToVoid(arena_, invoke); 853} 854void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) { 855 CreateIntIntIntIntToVoid(arena_, invoke); 856} 857void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) { 858 CreateIntIntIntIntToVoid(arena_, invoke); 859} 860void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) { 861 CreateIntIntIntIntToVoid(arena_, invoke); 862} 863void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) { 864 CreateIntIntIntIntToVoid(arena_, invoke); 865} 866void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) { 867 CreateIntIntIntIntToVoid(arena_, invoke); 868} 869void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) { 870 CreateIntIntIntIntToVoid(arena_, invoke); 871} 872void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) { 873 CreateIntIntIntIntToVoid(arena_, invoke); 874} 875 876static void GenUnsafePut(LocationSummary* locations, 877 Primitive::Type type, 878 bool is_volatile, 879 bool is_ordered, 880 CodeGeneratorARM64* codegen) { 881 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_; 882 883 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer. 884 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset. 885 Register value = RegisterFrom(locations->InAt(3), type); 886 Register source = value; 887 bool use_acquire_release = codegen->GetInstructionSetFeatures().PreferAcquireRelease(); 888 889 MemOperand mem_op(base.X(), offset); 890 891 { 892 // We use a block to end the scratch scope before the write barrier, thus 893 // freeing the temporary registers so they can be used in `MarkGCCard`. 894 UseScratchRegisterScope temps(masm); 895 896 if (kPoisonHeapReferences && type == Primitive::kPrimNot) { 897 DCHECK(value.IsW()); 898 Register temp = temps.AcquireW(); 899 __ Mov(temp.W(), value.W()); 900 codegen->GetAssembler()->PoisonHeapReference(temp.W()); 901 source = temp; 902 } 903 904 if (is_volatile || is_ordered) { 905 if (use_acquire_release) { 906 codegen->StoreRelease(type, source, mem_op); 907 } else { 908 __ Dmb(InnerShareable, BarrierAll); 909 codegen->Store(type, source, mem_op); 910 if (is_volatile) { 911 __ Dmb(InnerShareable, BarrierReads); 912 } 913 } 914 } else { 915 codegen->Store(type, source, mem_op); 916 } 917 } 918 919 if (type == Primitive::kPrimNot) { 920 bool value_can_be_null = true; // TODO: Worth finding out this information? 921 codegen->MarkGCCard(base, value, value_can_be_null); 922 } 923} 924 925void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) { 926 GenUnsafePut(invoke->GetLocations(), 927 Primitive::kPrimInt, 928 /* is_volatile */ false, 929 /* is_ordered */ false, 930 codegen_); 931} 932void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) { 933 GenUnsafePut(invoke->GetLocations(), 934 Primitive::kPrimInt, 935 /* is_volatile */ false, 936 /* is_ordered */ true, 937 codegen_); 938} 939void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) { 940 GenUnsafePut(invoke->GetLocations(), 941 Primitive::kPrimInt, 942 /* is_volatile */ true, 943 /* is_ordered */ false, 944 codegen_); 945} 946void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) { 947 GenUnsafePut(invoke->GetLocations(), 948 Primitive::kPrimNot, 949 /* is_volatile */ false, 950 /* is_ordered */ false, 951 codegen_); 952} 953void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) { 954 GenUnsafePut(invoke->GetLocations(), 955 Primitive::kPrimNot, 956 /* is_volatile */ false, 957 /* is_ordered */ true, 958 codegen_); 959} 960void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) { 961 GenUnsafePut(invoke->GetLocations(), 962 Primitive::kPrimNot, 963 /* is_volatile */ true, 964 /* is_ordered */ false, 965 codegen_); 966} 967void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) { 968 GenUnsafePut(invoke->GetLocations(), 969 Primitive::kPrimLong, 970 /* is_volatile */ false, 971 /* is_ordered */ false, 972 codegen_); 973} 974void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) { 975 GenUnsafePut(invoke->GetLocations(), 976 Primitive::kPrimLong, 977 /* is_volatile */ false, 978 /* is_ordered */ true, 979 codegen_); 980} 981void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) { 982 GenUnsafePut(invoke->GetLocations(), 983 Primitive::kPrimLong, 984 /* is_volatile */ true, 985 /* is_ordered */ false, 986 codegen_); 987} 988 989static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, 990 HInvoke* invoke, 991 Primitive::Type type) { 992 LocationSummary* locations = new (arena) LocationSummary(invoke, 993 LocationSummary::kNoCall, 994 kIntrinsified); 995 locations->SetInAt(0, Location::NoLocation()); // Unused receiver. 996 locations->SetInAt(1, Location::RequiresRegister()); 997 locations->SetInAt(2, Location::RequiresRegister()); 998 locations->SetInAt(3, Location::RequiresRegister()); 999 locations->SetInAt(4, Location::RequiresRegister()); 1000 1001 // If heap poisoning is enabled, we don't want the unpoisoning 1002 // operations to potentially clobber the output. 1003 Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot) 1004 ? Location::kOutputOverlap 1005 : Location::kNoOutputOverlap; 1006 locations->SetOut(Location::RequiresRegister(), overlaps); 1007} 1008 1009static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) { 1010 bool use_acquire_release = codegen->GetInstructionSetFeatures().PreferAcquireRelease(); 1011 vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_; 1012 1013 Register out = WRegisterFrom(locations->Out()); // Boolean result. 1014 1015 Register base = WRegisterFrom(locations->InAt(1)); // Object pointer. 1016 Register offset = XRegisterFrom(locations->InAt(2)); // Long offset. 1017 Register expected = RegisterFrom(locations->InAt(3), type); // Expected. 1018 Register value = RegisterFrom(locations->InAt(4), type); // Value. 1019 1020 // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps. 1021 if (type == Primitive::kPrimNot) { 1022 // Mark card for object assuming new value is stored. 1023 bool value_can_be_null = true; // TODO: Worth finding out this information? 1024 codegen->MarkGCCard(base, value, value_can_be_null); 1025 } 1026 1027 UseScratchRegisterScope temps(masm); 1028 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory. 1029 Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory. 1030 1031 Register tmp_32 = tmp_value.W(); 1032 1033 __ Add(tmp_ptr, base.X(), Operand(offset)); 1034 1035 if (kPoisonHeapReferences && type == Primitive::kPrimNot) { 1036 codegen->GetAssembler()->PoisonHeapReference(expected); 1037 if (value.Is(expected)) { 1038 // Do not poison `value`, as it is the same register as 1039 // `expected`, which has just been poisoned. 1040 } else { 1041 codegen->GetAssembler()->PoisonHeapReference(value); 1042 } 1043 } 1044 1045 // do { 1046 // tmp_value = [tmp_ptr] - expected; 1047 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value)); 1048 // result = tmp_value != 0; 1049 1050 vixl::Label loop_head, exit_loop; 1051 if (use_acquire_release) { 1052 __ Bind(&loop_head); 1053 // TODO: When `type == Primitive::kPrimNot`, add a read barrier for 1054 // the reference stored in the object before attempting the CAS, 1055 // similar to the one in the art::Unsafe_compareAndSwapObject JNI 1056 // implementation. 1057 // 1058 // Note that this code is not (yet) used when read barriers are 1059 // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject). 1060 DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier)); 1061 __ Ldaxr(tmp_value, MemOperand(tmp_ptr)); 1062 __ Cmp(tmp_value, expected); 1063 __ B(&exit_loop, ne); 1064 __ Stlxr(tmp_32, value, MemOperand(tmp_ptr)); 1065 __ Cbnz(tmp_32, &loop_head); 1066 } else { 1067 // Emit a `Dmb(InnerShareable, BarrierAll)` (DMB ISH) instruction 1068 // instead of a `Dmb(InnerShareable, BarrierWrites)` (DMB ISHST) 1069 // one, as the latter allows a preceding load to be delayed past 1070 // the STXR instruction below. 1071 __ Dmb(InnerShareable, BarrierAll); 1072 __ Bind(&loop_head); 1073 // TODO: When `type == Primitive::kPrimNot`, add a read barrier for 1074 // the reference stored in the object before attempting the CAS, 1075 // similar to the one in the art::Unsafe_compareAndSwapObject JNI 1076 // implementation. 1077 // 1078 // Note that this code is not (yet) used when read barriers are 1079 // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject). 1080 DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier)); 1081 __ Ldxr(tmp_value, MemOperand(tmp_ptr)); 1082 __ Cmp(tmp_value, expected); 1083 __ B(&exit_loop, ne); 1084 __ Stxr(tmp_32, value, MemOperand(tmp_ptr)); 1085 __ Cbnz(tmp_32, &loop_head); 1086 __ Dmb(InnerShareable, BarrierAll); 1087 } 1088 __ Bind(&exit_loop); 1089 __ Cset(out, eq); 1090 1091 if (kPoisonHeapReferences && type == Primitive::kPrimNot) { 1092 codegen->GetAssembler()->UnpoisonHeapReference(expected); 1093 if (value.Is(expected)) { 1094 // Do not unpoison `value`, as it is the same register as 1095 // `expected`, which has just been unpoisoned. 1096 } else { 1097 codegen->GetAssembler()->UnpoisonHeapReference(value); 1098 } 1099 } 1100} 1101 1102void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) { 1103 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt); 1104} 1105void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) { 1106 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong); 1107} 1108void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) { 1109 // The UnsafeCASObject intrinsic is missing a read barrier, and 1110 // therefore sometimes does not work as expected (b/25883050). 1111 // Turn it off temporarily as a quick fix, until the read barrier is 1112 // implemented (see TODO in GenCAS below). 1113 // 1114 // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers. 1115 if (kEmitCompilerReadBarrier) { 1116 return; 1117 } 1118 1119 CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot); 1120} 1121 1122void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) { 1123 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_); 1124} 1125void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) { 1126 GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_); 1127} 1128void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) { 1129 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); 1130} 1131 1132void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) { 1133 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1134 LocationSummary::kCallOnSlowPath, 1135 kIntrinsified); 1136 locations->SetInAt(0, Location::RequiresRegister()); 1137 locations->SetInAt(1, Location::RequiresRegister()); 1138 // In case we need to go in the slow path, we can't have the output be the same 1139 // as the input: the current liveness analysis considers the input to be live 1140 // at the point of the call. 1141 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 1142} 1143 1144void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) { 1145 vixl::MacroAssembler* masm = GetVIXLAssembler(); 1146 LocationSummary* locations = invoke->GetLocations(); 1147 1148 // Location of reference to data array 1149 const MemberOffset value_offset = mirror::String::ValueOffset(); 1150 // Location of count 1151 const MemberOffset count_offset = mirror::String::CountOffset(); 1152 1153 Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer. 1154 Register idx = WRegisterFrom(locations->InAt(1)); // Index of character. 1155 Register out = WRegisterFrom(locations->Out()); // Result character. 1156 1157 UseScratchRegisterScope temps(masm); 1158 Register temp = temps.AcquireW(); 1159 Register array_temp = temps.AcquireW(); // We can trade this for worse scheduling. 1160 1161 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth 1162 // the cost. 1163 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick 1164 // we will not optimize the code for constants (which would save a register). 1165 1166 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); 1167 codegen_->AddSlowPath(slow_path); 1168 1169 __ Ldr(temp, HeapOperand(obj, count_offset)); // temp = str.length. 1170 codegen_->MaybeRecordImplicitNullCheck(invoke); 1171 __ Cmp(idx, temp); 1172 __ B(hs, slow_path->GetEntryLabel()); 1173 1174 __ Add(array_temp, obj, Operand(value_offset.Int32Value())); // array_temp := str.value. 1175 1176 // Load the value. 1177 __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1)); // out := array_temp[idx]. 1178 1179 __ Bind(slow_path->GetExitLabel()); 1180} 1181 1182void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) { 1183 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1184 LocationSummary::kCall, 1185 kIntrinsified); 1186 InvokeRuntimeCallingConvention calling_convention; 1187 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); 1188 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); 1189 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt)); 1190} 1191 1192void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { 1193 vixl::MacroAssembler* masm = GetVIXLAssembler(); 1194 LocationSummary* locations = invoke->GetLocations(); 1195 1196 // Note that the null check must have been done earlier. 1197 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); 1198 1199 Register argument = WRegisterFrom(locations->InAt(1)); 1200 __ Cmp(argument, 0); 1201 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); 1202 codegen_->AddSlowPath(slow_path); 1203 __ B(eq, slow_path->GetEntryLabel()); 1204 1205 __ Ldr( 1206 lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pStringCompareTo).Int32Value())); 1207 __ Blr(lr); 1208 __ Bind(slow_path->GetExitLabel()); 1209} 1210 1211void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) { 1212 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1213 LocationSummary::kNoCall, 1214 kIntrinsified); 1215 locations->SetInAt(0, Location::RequiresRegister()); 1216 locations->SetInAt(1, Location::RequiresRegister()); 1217 // Temporary registers to store lengths of strings and for calculations. 1218 locations->AddTemp(Location::RequiresRegister()); 1219 locations->AddTemp(Location::RequiresRegister()); 1220 1221 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 1222} 1223 1224void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) { 1225 vixl::MacroAssembler* masm = GetVIXLAssembler(); 1226 LocationSummary* locations = invoke->GetLocations(); 1227 1228 Register str = WRegisterFrom(locations->InAt(0)); 1229 Register arg = WRegisterFrom(locations->InAt(1)); 1230 Register out = XRegisterFrom(locations->Out()); 1231 1232 UseScratchRegisterScope scratch_scope(masm); 1233 Register temp = scratch_scope.AcquireW(); 1234 Register temp1 = WRegisterFrom(locations->GetTemp(0)); 1235 Register temp2 = WRegisterFrom(locations->GetTemp(1)); 1236 1237 vixl::Label loop; 1238 vixl::Label end; 1239 vixl::Label return_true; 1240 vixl::Label return_false; 1241 1242 // Get offsets of count, value, and class fields within a string object. 1243 const int32_t count_offset = mirror::String::CountOffset().Int32Value(); 1244 const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); 1245 const int32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 1246 1247 // Note that the null check must have been done earlier. 1248 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); 1249 1250 // Check if input is null, return false if it is. 1251 __ Cbz(arg, &return_false); 1252 1253 // Reference equality check, return true if same reference. 1254 __ Cmp(str, arg); 1255 __ B(&return_true, eq); 1256 1257 // Instanceof check for the argument by comparing class fields. 1258 // All string objects must have the same type since String cannot be subclassed. 1259 // Receiver must be a string object, so its class field is equal to all strings' class fields. 1260 // If the argument is a string object, its class field must be equal to receiver's class field. 1261 __ Ldr(temp, MemOperand(str.X(), class_offset)); 1262 __ Ldr(temp1, MemOperand(arg.X(), class_offset)); 1263 __ Cmp(temp, temp1); 1264 __ B(&return_false, ne); 1265 1266 // Load lengths of this and argument strings. 1267 __ Ldr(temp, MemOperand(str.X(), count_offset)); 1268 __ Ldr(temp1, MemOperand(arg.X(), count_offset)); 1269 // Check if lengths are equal, return false if they're not. 1270 __ Cmp(temp, temp1); 1271 __ B(&return_false, ne); 1272 // Store offset of string value in preparation for comparison loop 1273 __ Mov(temp1, value_offset); 1274 // Return true if both strings are empty. 1275 __ Cbz(temp, &return_true); 1276 1277 // Assertions that must hold in order to compare strings 4 characters at a time. 1278 DCHECK_ALIGNED(value_offset, 8); 1279 static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded"); 1280 1281 temp1 = temp1.X(); 1282 temp2 = temp2.X(); 1283 1284 // Loop to compare strings 4 characters at a time starting at the beginning of the string. 1285 // Ok to do this because strings are zero-padded to be 8-byte aligned. 1286 __ Bind(&loop); 1287 __ Ldr(out, MemOperand(str.X(), temp1)); 1288 __ Ldr(temp2, MemOperand(arg.X(), temp1)); 1289 __ Add(temp1, temp1, Operand(sizeof(uint64_t))); 1290 __ Cmp(out, temp2); 1291 __ B(&return_false, ne); 1292 __ Sub(temp, temp, Operand(4), SetFlags); 1293 __ B(&loop, gt); 1294 1295 // Return true and exit the function. 1296 // If loop does not result in returning false, we return true. 1297 __ Bind(&return_true); 1298 __ Mov(out, 1); 1299 __ B(&end); 1300 1301 // Return false and exit the function. 1302 __ Bind(&return_false); 1303 __ Mov(out, 0); 1304 __ Bind(&end); 1305} 1306 1307static void GenerateVisitStringIndexOf(HInvoke* invoke, 1308 vixl::MacroAssembler* masm, 1309 CodeGeneratorARM64* codegen, 1310 ArenaAllocator* allocator, 1311 bool start_at_zero) { 1312 LocationSummary* locations = invoke->GetLocations(); 1313 Register tmp_reg = WRegisterFrom(locations->GetTemp(0)); 1314 1315 // Note that the null check must have been done earlier. 1316 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); 1317 1318 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically, 1319 // or directly dispatch if we have a constant. 1320 SlowPathCodeARM64* slow_path = nullptr; 1321 if (invoke->InputAt(1)->IsIntConstant()) { 1322 if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > 0xFFFFU) { 1323 // Always needs the slow-path. We could directly dispatch to it, but this case should be 1324 // rare, so for simplicity just put the full slow-path down and branch unconditionally. 1325 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke); 1326 codegen->AddSlowPath(slow_path); 1327 __ B(slow_path->GetEntryLabel()); 1328 __ Bind(slow_path->GetExitLabel()); 1329 return; 1330 } 1331 } else { 1332 Register char_reg = WRegisterFrom(locations->InAt(1)); 1333 __ Mov(tmp_reg, 0xFFFF); 1334 __ Cmp(char_reg, Operand(tmp_reg)); 1335 slow_path = new (allocator) IntrinsicSlowPathARM64(invoke); 1336 codegen->AddSlowPath(slow_path); 1337 __ B(hi, slow_path->GetEntryLabel()); 1338 } 1339 1340 if (start_at_zero) { 1341 // Start-index = 0. 1342 __ Mov(tmp_reg, 0); 1343 } 1344 1345 __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pIndexOf).Int32Value())); 1346 __ Blr(lr); 1347 1348 if (slow_path != nullptr) { 1349 __ Bind(slow_path->GetExitLabel()); 1350 } 1351} 1352 1353void IntrinsicLocationsBuilderARM64::VisitStringIndexOf(HInvoke* invoke) { 1354 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1355 LocationSummary::kCall, 1356 kIntrinsified); 1357 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's 1358 // best to align the inputs accordingly. 1359 InvokeRuntimeCallingConvention calling_convention; 1360 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); 1361 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); 1362 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt)); 1363 1364 // Need a temp for slow-path codepoint compare, and need to send start_index=0. 1365 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2))); 1366} 1367 1368void IntrinsicCodeGeneratorARM64::VisitStringIndexOf(HInvoke* invoke) { 1369 GenerateVisitStringIndexOf( 1370 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true); 1371} 1372 1373void IntrinsicLocationsBuilderARM64::VisitStringIndexOfAfter(HInvoke* invoke) { 1374 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1375 LocationSummary::kCall, 1376 kIntrinsified); 1377 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's 1378 // best to align the inputs accordingly. 1379 InvokeRuntimeCallingConvention calling_convention; 1380 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); 1381 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); 1382 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); 1383 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt)); 1384 1385 // Need a temp for slow-path codepoint compare. 1386 locations->AddTemp(Location::RequiresRegister()); 1387} 1388 1389void IntrinsicCodeGeneratorARM64::VisitStringIndexOfAfter(HInvoke* invoke) { 1390 GenerateVisitStringIndexOf( 1391 invoke, GetVIXLAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false); 1392} 1393 1394void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) { 1395 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1396 LocationSummary::kCall, 1397 kIntrinsified); 1398 InvokeRuntimeCallingConvention calling_convention; 1399 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); 1400 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); 1401 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); 1402 locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3))); 1403 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); 1404} 1405 1406void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) { 1407 vixl::MacroAssembler* masm = GetVIXLAssembler(); 1408 LocationSummary* locations = invoke->GetLocations(); 1409 1410 Register byte_array = WRegisterFrom(locations->InAt(0)); 1411 __ Cmp(byte_array, 0); 1412 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); 1413 codegen_->AddSlowPath(slow_path); 1414 __ B(eq, slow_path->GetEntryLabel()); 1415 1416 __ Ldr(lr, 1417 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value())); 1418 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1419 __ Blr(lr); 1420 __ Bind(slow_path->GetExitLabel()); 1421} 1422 1423void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) { 1424 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1425 LocationSummary::kCall, 1426 kIntrinsified); 1427 InvokeRuntimeCallingConvention calling_convention; 1428 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); 1429 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); 1430 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); 1431 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); 1432} 1433 1434void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) { 1435 vixl::MacroAssembler* masm = GetVIXLAssembler(); 1436 1437 __ Ldr(lr, 1438 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value())); 1439 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1440 __ Blr(lr); 1441} 1442 1443void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) { 1444 // The inputs plus one temp. 1445 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1446 LocationSummary::kCall, 1447 kIntrinsified); 1448 InvokeRuntimeCallingConvention calling_convention; 1449 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); 1450 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); 1451 locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); 1452 locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); 1453} 1454 1455void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) { 1456 vixl::MacroAssembler* masm = GetVIXLAssembler(); 1457 LocationSummary* locations = invoke->GetLocations(); 1458 1459 Register string_to_copy = WRegisterFrom(locations->InAt(0)); 1460 __ Cmp(string_to_copy, 0); 1461 SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); 1462 codegen_->AddSlowPath(slow_path); 1463 __ B(eq, slow_path->GetEntryLabel()); 1464 1465 __ Ldr(lr, 1466 MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value())); 1467 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1468 __ Blr(lr); 1469 __ Bind(slow_path->GetExitLabel()); 1470} 1471 1472// Unimplemented intrinsics. 1473 1474#define UNIMPLEMENTED_INTRINSIC(Name) \ 1475void IntrinsicLocationsBuilderARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ 1476} \ 1477void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ 1478} 1479 1480UNIMPLEMENTED_INTRINSIC(IntegerBitCount) 1481UNIMPLEMENTED_INTRINSIC(LongBitCount) 1482UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) 1483UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) 1484UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) 1485UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) 1486 1487UNIMPLEMENTED_INTRINSIC(MathCos) 1488UNIMPLEMENTED_INTRINSIC(MathSin) 1489UNIMPLEMENTED_INTRINSIC(MathAcos) 1490UNIMPLEMENTED_INTRINSIC(MathAsin) 1491UNIMPLEMENTED_INTRINSIC(MathAtan) 1492UNIMPLEMENTED_INTRINSIC(MathAtan2) 1493UNIMPLEMENTED_INTRINSIC(MathCbrt) 1494UNIMPLEMENTED_INTRINSIC(MathCosh) 1495UNIMPLEMENTED_INTRINSIC(MathExp) 1496UNIMPLEMENTED_INTRINSIC(MathExpm1) 1497UNIMPLEMENTED_INTRINSIC(MathHypot) 1498UNIMPLEMENTED_INTRINSIC(MathLog) 1499UNIMPLEMENTED_INTRINSIC(MathLog10) 1500UNIMPLEMENTED_INTRINSIC(MathNextAfter) 1501UNIMPLEMENTED_INTRINSIC(MathSinh) 1502UNIMPLEMENTED_INTRINSIC(MathTan) 1503UNIMPLEMENTED_INTRINSIC(MathTanh) 1504 1505UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) 1506UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) 1507UNIMPLEMENTED_INTRINSIC(FloatIsNaN) 1508UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) 1509 1510UNIMPLEMENTED_INTRINSIC(IntegerCompare) 1511UNIMPLEMENTED_INTRINSIC(LongCompare) 1512UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) 1513UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) 1514UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) 1515UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) 1516UNIMPLEMENTED_INTRINSIC(IntegerSignum) 1517UNIMPLEMENTED_INTRINSIC(LongSignum) 1518 1519// Rotate operations are handled as HRor instructions. 1520UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) 1521UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) 1522UNIMPLEMENTED_INTRINSIC(LongRotateLeft) 1523UNIMPLEMENTED_INTRINSIC(LongRotateRight) 1524 1525#undef UNIMPLEMENTED_INTRINSIC 1526 1527#undef __ 1528 1529} // namespace arm64 1530} // namespace art 1531