intrinsics_arm.cc revision 4ab02352db4051d590b793f34d166a0b5c633c4a
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_arm.h" 18 19#include "arch/arm/instruction_set_features_arm.h" 20#include "art_method.h" 21#include "code_generator_arm.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/arm/assembler_arm.h" 28 29namespace art { 30 31namespace arm { 32 33ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() { 34 return codegen_->GetAssembler(); 35} 36 37ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() { 38 return codegen_->GetGraph()->GetArena(); 39} 40 41#define __ codegen->GetAssembler()-> 42 43static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGeneratorARM* codegen) { 44 if (!trg.IsValid()) { 45 DCHECK(type == Primitive::kPrimVoid); 46 return; 47 } 48 49 DCHECK_NE(type, Primitive::kPrimVoid); 50 51 if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) { 52 if (type == Primitive::kPrimLong) { 53 Register trg_reg_lo = trg.AsRegisterPairLow<Register>(); 54 Register trg_reg_hi = trg.AsRegisterPairHigh<Register>(); 55 Register res_reg_lo = R0; 56 Register res_reg_hi = R1; 57 if (trg_reg_lo != res_reg_hi) { 58 if (trg_reg_lo != res_reg_lo) { 59 __ mov(trg_reg_lo, ShifterOperand(res_reg_lo)); 60 __ mov(trg_reg_hi, ShifterOperand(res_reg_hi)); 61 } else { 62 DCHECK_EQ(trg_reg_lo + 1, trg_reg_hi); 63 } 64 } else { 65 __ mov(trg_reg_hi, ShifterOperand(res_reg_hi)); 66 __ mov(trg_reg_lo, ShifterOperand(res_reg_lo)); 67 } 68 } else { 69 Register trg_reg = trg.AsRegister<Register>(); 70 Register res_reg = R0; 71 if (trg_reg != res_reg) { 72 __ mov(trg_reg, ShifterOperand(res_reg)); 73 } 74 } 75 } else { 76 UNIMPLEMENTED(FATAL) << "Floating-point return."; 77 } 78} 79 80static void MoveArguments(HInvoke* invoke, CodeGeneratorARM* codegen) { 81 InvokeDexCallingConventionVisitorARM calling_convention_visitor; 82 IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor); 83} 84 85// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified 86// call. This will copy the arguments into the positions for a regular call. 87// 88// Note: The actual parameters are required to be in the locations given by the invoke's location 89// summary. If an intrinsic modifies those locations before a slowpath call, they must be 90// restored! 91class IntrinsicSlowPathARM : public SlowPathCodeARM { 92 public: 93 explicit IntrinsicSlowPathARM(HInvoke* invoke) : invoke_(invoke) { } 94 95 void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { 96 CodeGeneratorARM* codegen = down_cast<CodeGeneratorARM*>(codegen_in); 97 __ Bind(GetEntryLabel()); 98 99 SaveLiveRegisters(codegen, invoke_->GetLocations()); 100 101 MoveArguments(invoke_, codegen); 102 103 if (invoke_->IsInvokeStaticOrDirect()) { 104 codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), 105 Location::RegisterLocation(kArtMethodRegister)); 106 codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); 107 } else { 108 UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented"; 109 UNREACHABLE(); 110 } 111 112 // Copy the result back to the expected output. 113 Location out = invoke_->GetLocations()->Out(); 114 if (out.IsValid()) { 115 DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory. 116 DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg())); 117 MoveFromReturnRegister(out, invoke_->GetType(), codegen); 118 } 119 120 RestoreLiveRegisters(codegen, invoke_->GetLocations()); 121 __ b(GetExitLabel()); 122 } 123 124 const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM"; } 125 126 private: 127 // The instruction where this slow path is happening. 128 HInvoke* const invoke_; 129 130 DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM); 131}; 132 133#undef __ 134 135bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) { 136 Dispatch(invoke); 137 LocationSummary* res = invoke->GetLocations(); 138 return res != nullptr && res->Intrinsified(); 139} 140 141#define __ assembler-> 142 143static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { 144 LocationSummary* locations = new (arena) LocationSummary(invoke, 145 LocationSummary::kNoCall, 146 kIntrinsified); 147 locations->SetInAt(0, Location::RequiresFpuRegister()); 148 locations->SetOut(Location::RequiresRegister()); 149} 150 151static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { 152 LocationSummary* locations = new (arena) LocationSummary(invoke, 153 LocationSummary::kNoCall, 154 kIntrinsified); 155 locations->SetInAt(0, Location::RequiresRegister()); 156 locations->SetOut(Location::RequiresFpuRegister()); 157} 158 159static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) { 160 Location input = locations->InAt(0); 161 Location output = locations->Out(); 162 if (is64bit) { 163 __ vmovrrd(output.AsRegisterPairLow<Register>(), 164 output.AsRegisterPairHigh<Register>(), 165 FromLowSToD(input.AsFpuRegisterPairLow<SRegister>())); 166 } else { 167 __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>()); 168 } 169} 170 171static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) { 172 Location input = locations->InAt(0); 173 Location output = locations->Out(); 174 if (is64bit) { 175 __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()), 176 input.AsRegisterPairLow<Register>(), 177 input.AsRegisterPairHigh<Register>()); 178 } else { 179 __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>()); 180 } 181} 182 183void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) { 184 CreateFPToIntLocations(arena_, invoke); 185} 186void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) { 187 CreateIntToFPLocations(arena_, invoke); 188} 189 190void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) { 191 MoveFPToInt(invoke->GetLocations(), true, GetAssembler()); 192} 193void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) { 194 MoveIntToFP(invoke->GetLocations(), true, GetAssembler()); 195} 196 197void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) { 198 CreateFPToIntLocations(arena_, invoke); 199} 200void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) { 201 CreateIntToFPLocations(arena_, invoke); 202} 203 204void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) { 205 MoveFPToInt(invoke->GetLocations(), false, GetAssembler()); 206} 207void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) { 208 MoveIntToFP(invoke->GetLocations(), false, GetAssembler()); 209} 210 211static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { 212 LocationSummary* locations = new (arena) LocationSummary(invoke, 213 LocationSummary::kNoCall, 214 kIntrinsified); 215 locations->SetInAt(0, Location::RequiresRegister()); 216 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 217} 218 219static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { 220 LocationSummary* locations = new (arena) LocationSummary(invoke, 221 LocationSummary::kNoCall, 222 kIntrinsified); 223 locations->SetInAt(0, Location::RequiresFpuRegister()); 224 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 225} 226 227static void GenNumberOfLeadingZeros(LocationSummary* locations, 228 Primitive::Type type, 229 ArmAssembler* assembler) { 230 Location in = locations->InAt(0); 231 Register out = locations->Out().AsRegister<Register>(); 232 233 DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong)); 234 235 if (type == Primitive::kPrimLong) { 236 Register in_reg_lo = in.AsRegisterPairLow<Register>(); 237 Register in_reg_hi = in.AsRegisterPairHigh<Register>(); 238 Label end; 239 __ clz(out, in_reg_hi); 240 __ CompareAndBranchIfNonZero(in_reg_hi, &end); 241 __ clz(out, in_reg_lo); 242 __ AddConstant(out, 32); 243 __ Bind(&end); 244 } else { 245 __ clz(out, in.AsRegister<Register>()); 246 } 247} 248 249void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { 250 CreateIntToIntLocations(arena_, invoke); 251} 252 253void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { 254 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); 255} 256 257void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { 258 LocationSummary* locations = new (arena_) LocationSummary(invoke, 259 LocationSummary::kNoCall, 260 kIntrinsified); 261 locations->SetInAt(0, Location::RequiresRegister()); 262 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 263} 264 265void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { 266 GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler()); 267} 268 269static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) { 270 Location in = locations->InAt(0); 271 Location out = locations->Out(); 272 273 if (is64bit) { 274 __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 275 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 276 } else { 277 __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>()); 278 } 279} 280 281void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) { 282 CreateFPToFPLocations(arena_, invoke); 283} 284 285void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) { 286 MathAbsFP(invoke->GetLocations(), true, GetAssembler()); 287} 288 289void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) { 290 CreateFPToFPLocations(arena_, invoke); 291} 292 293void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) { 294 MathAbsFP(invoke->GetLocations(), false, GetAssembler()); 295} 296 297static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) { 298 LocationSummary* locations = new (arena) LocationSummary(invoke, 299 LocationSummary::kNoCall, 300 kIntrinsified); 301 locations->SetInAt(0, Location::RequiresRegister()); 302 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 303 304 locations->AddTemp(Location::RequiresRegister()); 305} 306 307static void GenAbsInteger(LocationSummary* locations, 308 bool is64bit, 309 ArmAssembler* assembler) { 310 Location in = locations->InAt(0); 311 Location output = locations->Out(); 312 313 Register mask = locations->GetTemp(0).AsRegister<Register>(); 314 315 if (is64bit) { 316 Register in_reg_lo = in.AsRegisterPairLow<Register>(); 317 Register in_reg_hi = in.AsRegisterPairHigh<Register>(); 318 Register out_reg_lo = output.AsRegisterPairLow<Register>(); 319 Register out_reg_hi = output.AsRegisterPairHigh<Register>(); 320 321 DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected."; 322 323 __ Asr(mask, in_reg_hi, 31); 324 __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask)); 325 __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask)); 326 __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo)); 327 __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi)); 328 } else { 329 Register in_reg = in.AsRegister<Register>(); 330 Register out_reg = output.AsRegister<Register>(); 331 332 __ Asr(mask, in_reg, 31); 333 __ add(out_reg, in_reg, ShifterOperand(mask)); 334 __ eor(out_reg, mask, ShifterOperand(out_reg)); 335 } 336} 337 338void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) { 339 CreateIntToIntPlusTemp(arena_, invoke); 340} 341 342void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) { 343 GenAbsInteger(invoke->GetLocations(), false, GetAssembler()); 344} 345 346 347void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) { 348 CreateIntToIntPlusTemp(arena_, invoke); 349} 350 351void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) { 352 GenAbsInteger(invoke->GetLocations(), true, GetAssembler()); 353} 354 355static void GenMinMax(LocationSummary* locations, 356 bool is_min, 357 ArmAssembler* assembler) { 358 Register op1 = locations->InAt(0).AsRegister<Register>(); 359 Register op2 = locations->InAt(1).AsRegister<Register>(); 360 Register out = locations->Out().AsRegister<Register>(); 361 362 __ cmp(op1, ShifterOperand(op2)); 363 364 __ it((is_min) ? Condition::LT : Condition::GT, kItElse); 365 __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT); 366 __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE); 367} 368 369static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { 370 LocationSummary* locations = new (arena) LocationSummary(invoke, 371 LocationSummary::kNoCall, 372 kIntrinsified); 373 locations->SetInAt(0, Location::RequiresRegister()); 374 locations->SetInAt(1, Location::RequiresRegister()); 375 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 376} 377 378void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) { 379 CreateIntIntToIntLocations(arena_, invoke); 380} 381 382void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) { 383 GenMinMax(invoke->GetLocations(), true, GetAssembler()); 384} 385 386void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) { 387 CreateIntIntToIntLocations(arena_, invoke); 388} 389 390void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) { 391 GenMinMax(invoke->GetLocations(), false, GetAssembler()); 392} 393 394void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) { 395 CreateFPToFPLocations(arena_, invoke); 396} 397 398void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) { 399 LocationSummary* locations = invoke->GetLocations(); 400 ArmAssembler* assembler = GetAssembler(); 401 __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()), 402 FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>())); 403} 404 405void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) { 406 CreateIntToIntLocations(arena_, invoke); 407} 408 409void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) { 410 ArmAssembler* assembler = GetAssembler(); 411 // Ignore upper 4B of long address. 412 __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(), 413 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>())); 414} 415 416void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) { 417 CreateIntToIntLocations(arena_, invoke); 418} 419 420void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) { 421 ArmAssembler* assembler = GetAssembler(); 422 // Ignore upper 4B of long address. 423 __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(), 424 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>())); 425} 426 427void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) { 428 CreateIntToIntLocations(arena_, invoke); 429} 430 431void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) { 432 ArmAssembler* assembler = GetAssembler(); 433 // Ignore upper 4B of long address. 434 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); 435 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor 436 // exception. So we can't use ldrd as addr may be unaligned. 437 Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>(); 438 Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>(); 439 if (addr == lo) { 440 __ ldr(hi, Address(addr, 4)); 441 __ ldr(lo, Address(addr, 0)); 442 } else { 443 __ ldr(lo, Address(addr, 0)); 444 __ ldr(hi, Address(addr, 4)); 445 } 446} 447 448void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) { 449 CreateIntToIntLocations(arena_, invoke); 450} 451 452void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) { 453 ArmAssembler* assembler = GetAssembler(); 454 // Ignore upper 4B of long address. 455 __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(), 456 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>())); 457} 458 459static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) { 460 LocationSummary* locations = new (arena) LocationSummary(invoke, 461 LocationSummary::kNoCall, 462 kIntrinsified); 463 locations->SetInAt(0, Location::RequiresRegister()); 464 locations->SetInAt(1, Location::RequiresRegister()); 465} 466 467void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) { 468 CreateIntIntToVoidLocations(arena_, invoke); 469} 470 471void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) { 472 ArmAssembler* assembler = GetAssembler(); 473 __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(), 474 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>())); 475} 476 477void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) { 478 CreateIntIntToVoidLocations(arena_, invoke); 479} 480 481void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) { 482 ArmAssembler* assembler = GetAssembler(); 483 __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(), 484 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>())); 485} 486 487void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) { 488 CreateIntIntToVoidLocations(arena_, invoke); 489} 490 491void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) { 492 ArmAssembler* assembler = GetAssembler(); 493 // Ignore upper 4B of long address. 494 Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>(); 495 // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor 496 // exception. So we can't use ldrd as addr may be unaligned. 497 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0)); 498 __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4)); 499} 500 501void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) { 502 CreateIntIntToVoidLocations(arena_, invoke); 503} 504 505void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) { 506 ArmAssembler* assembler = GetAssembler(); 507 __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(), 508 Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>())); 509} 510 511void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) { 512 LocationSummary* locations = new (arena_) LocationSummary(invoke, 513 LocationSummary::kNoCall, 514 kIntrinsified); 515 locations->SetOut(Location::RequiresRegister()); 516} 517 518void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) { 519 ArmAssembler* assembler = GetAssembler(); 520 __ LoadFromOffset(kLoadWord, 521 invoke->GetLocations()->Out().AsRegister<Register>(), 522 TR, 523 Thread::PeerOffset<kArmPointerSize>().Int32Value()); 524} 525 526static void GenUnsafeGet(HInvoke* invoke, 527 Primitive::Type type, 528 bool is_volatile, 529 CodeGeneratorARM* codegen) { 530 LocationSummary* locations = invoke->GetLocations(); 531 DCHECK((type == Primitive::kPrimInt) || 532 (type == Primitive::kPrimLong) || 533 (type == Primitive::kPrimNot)); 534 ArmAssembler* assembler = codegen->GetAssembler(); 535 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer. 536 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only. 537 538 if (type == Primitive::kPrimLong) { 539 Register trg_lo = locations->Out().AsRegisterPairLow<Register>(); 540 __ add(IP, base, ShifterOperand(offset)); 541 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) { 542 Register trg_hi = locations->Out().AsRegisterPairHigh<Register>(); 543 __ ldrexd(trg_lo, trg_hi, IP); 544 } else { 545 __ ldrd(trg_lo, Address(IP)); 546 } 547 } else { 548 Register trg = locations->Out().AsRegister<Register>(); 549 __ ldr(trg, Address(base, offset)); 550 } 551 552 if (is_volatile) { 553 __ dmb(ISH); 554 } 555 556 if (type == Primitive::kPrimNot) { 557 Register trg = locations->Out().AsRegister<Register>(); 558 __ MaybeUnpoisonHeapReference(trg); 559 } 560} 561 562static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { 563 LocationSummary* locations = new (arena) LocationSummary(invoke, 564 LocationSummary::kNoCall, 565 kIntrinsified); 566 locations->SetInAt(0, Location::NoLocation()); // Unused receiver. 567 locations->SetInAt(1, Location::RequiresRegister()); 568 locations->SetInAt(2, Location::RequiresRegister()); 569 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 570} 571 572void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) { 573 CreateIntIntIntToIntLocations(arena_, invoke); 574} 575void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) { 576 CreateIntIntIntToIntLocations(arena_, invoke); 577} 578void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) { 579 CreateIntIntIntToIntLocations(arena_, invoke); 580} 581void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) { 582 CreateIntIntIntToIntLocations(arena_, invoke); 583} 584void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) { 585 CreateIntIntIntToIntLocations(arena_, invoke); 586} 587void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { 588 CreateIntIntIntToIntLocations(arena_, invoke); 589} 590 591void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) { 592 GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_); 593} 594void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) { 595 GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_); 596} 597void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) { 598 GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_); 599} 600void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) { 601 GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_); 602} 603void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) { 604 GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_); 605} 606void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { 607 GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_); 608} 609 610static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, 611 const ArmInstructionSetFeatures& features, 612 Primitive::Type type, 613 bool is_volatile, 614 HInvoke* invoke) { 615 LocationSummary* locations = new (arena) LocationSummary(invoke, 616 LocationSummary::kNoCall, 617 kIntrinsified); 618 locations->SetInAt(0, Location::NoLocation()); // Unused receiver. 619 locations->SetInAt(1, Location::RequiresRegister()); 620 locations->SetInAt(2, Location::RequiresRegister()); 621 locations->SetInAt(3, Location::RequiresRegister()); 622 623 if (type == Primitive::kPrimLong) { 624 // Potentially need temps for ldrexd-strexd loop. 625 if (is_volatile && !features.HasAtomicLdrdAndStrd()) { 626 locations->AddTemp(Location::RequiresRegister()); // Temp_lo. 627 locations->AddTemp(Location::RequiresRegister()); // Temp_hi. 628 } 629 } else if (type == Primitive::kPrimNot) { 630 // Temps for card-marking. 631 locations->AddTemp(Location::RequiresRegister()); // Temp. 632 locations->AddTemp(Location::RequiresRegister()); // Card. 633 } 634} 635 636void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) { 637 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke); 638} 639void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) { 640 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke); 641} 642void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) { 643 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, true, invoke); 644} 645void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) { 646 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke); 647} 648void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) { 649 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke); 650} 651void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) { 652 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, true, invoke); 653} 654void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) { 655 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke); 656} 657void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) { 658 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke); 659} 660void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) { 661 CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, true, invoke); 662} 663 664static void GenUnsafePut(LocationSummary* locations, 665 Primitive::Type type, 666 bool is_volatile, 667 bool is_ordered, 668 CodeGeneratorARM* codegen) { 669 ArmAssembler* assembler = codegen->GetAssembler(); 670 671 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer. 672 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only. 673 Register value; 674 675 if (is_volatile || is_ordered) { 676 __ dmb(ISH); 677 } 678 679 if (type == Primitive::kPrimLong) { 680 Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>(); 681 value = value_lo; 682 if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) { 683 Register temp_lo = locations->GetTemp(0).AsRegister<Register>(); 684 Register temp_hi = locations->GetTemp(1).AsRegister<Register>(); 685 Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>(); 686 687 __ add(IP, base, ShifterOperand(offset)); 688 Label loop_head; 689 __ Bind(&loop_head); 690 __ ldrexd(temp_lo, temp_hi, IP); 691 __ strexd(temp_lo, value_lo, value_hi, IP); 692 __ cmp(temp_lo, ShifterOperand(0)); 693 __ b(&loop_head, NE); 694 } else { 695 __ add(IP, base, ShifterOperand(offset)); 696 __ strd(value_lo, Address(IP)); 697 } 698 } else { 699 value = locations->InAt(3).AsRegister<Register>(); 700 Register source = value; 701 if (kPoisonHeapReferences && type == Primitive::kPrimNot) { 702 Register temp = locations->GetTemp(0).AsRegister<Register>(); 703 __ Mov(temp, value); 704 __ PoisonHeapReference(temp); 705 source = temp; 706 } 707 __ str(source, Address(base, offset)); 708 } 709 710 if (is_volatile) { 711 __ dmb(ISH); 712 } 713 714 if (type == Primitive::kPrimNot) { 715 Register temp = locations->GetTemp(0).AsRegister<Register>(); 716 Register card = locations->GetTemp(1).AsRegister<Register>(); 717 bool value_can_be_null = true; // TODO: Worth finding out this information? 718 codegen->MarkGCCard(temp, card, base, value, value_can_be_null); 719 } 720} 721 722void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) { 723 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_); 724} 725void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) { 726 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_); 727} 728void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) { 729 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_); 730} 731void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) { 732 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_); 733} 734void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) { 735 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_); 736} 737void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) { 738 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_); 739} 740void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) { 741 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_); 742} 743void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) { 744 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_); 745} 746void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) { 747 GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_); 748} 749 750static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena, 751 HInvoke* invoke) { 752 LocationSummary* locations = new (arena) LocationSummary(invoke, 753 LocationSummary::kNoCall, 754 kIntrinsified); 755 locations->SetInAt(0, Location::NoLocation()); // Unused receiver. 756 locations->SetInAt(1, Location::RequiresRegister()); 757 locations->SetInAt(2, Location::RequiresRegister()); 758 locations->SetInAt(3, Location::RequiresRegister()); 759 locations->SetInAt(4, Location::RequiresRegister()); 760 761 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 762 763 locations->AddTemp(Location::RequiresRegister()); // Pointer. 764 locations->AddTemp(Location::RequiresRegister()); // Temp 1. 765 locations->AddTemp(Location::RequiresRegister()); // Temp 2. 766} 767 768static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) { 769 DCHECK_NE(type, Primitive::kPrimLong); 770 771 ArmAssembler* assembler = codegen->GetAssembler(); 772 773 Register out = locations->Out().AsRegister<Register>(); // Boolean result. 774 775 Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer. 776 Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Offset (discard high 4B). 777 Register expected_lo = locations->InAt(3).AsRegister<Register>(); // Expected. 778 Register value_lo = locations->InAt(4).AsRegister<Register>(); // Value. 779 780 Register tmp_ptr = locations->GetTemp(0).AsRegister<Register>(); // Pointer to actual memory. 781 Register tmp_lo = locations->GetTemp(1).AsRegister<Register>(); // Value in memory. 782 783 if (type == Primitive::kPrimNot) { 784 // Mark card for object assuming new value is stored. Worst case we will mark an unchanged 785 // object and scan the receiver at the next GC for nothing. 786 bool value_can_be_null = true; // TODO: Worth finding out this information? 787 codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo, value_can_be_null); 788 } 789 790 // Prevent reordering with prior memory operations. 791 __ dmb(ISH); 792 793 __ add(tmp_ptr, base, ShifterOperand(offset)); 794 795 if (kPoisonHeapReferences && type == Primitive::kPrimNot) { 796 codegen->GetAssembler()->PoisonHeapReference(expected_lo); 797 codegen->GetAssembler()->PoisonHeapReference(value_lo); 798 } 799 800 // do { 801 // tmp = [r_ptr] - expected; 802 // } while (tmp == 0 && failure([r_ptr] <- r_new_value)); 803 // result = tmp != 0; 804 805 Label loop_head; 806 __ Bind(&loop_head); 807 808 __ ldrex(tmp_lo, tmp_ptr); 809 810 __ subs(tmp_lo, tmp_lo, ShifterOperand(expected_lo)); 811 812 __ it(EQ, ItState::kItT); 813 __ strex(tmp_lo, value_lo, tmp_ptr, EQ); 814 __ cmp(tmp_lo, ShifterOperand(1), EQ); 815 816 __ b(&loop_head, EQ); 817 818 __ dmb(ISH); 819 820 __ rsbs(out, tmp_lo, ShifterOperand(1)); 821 __ it(CC); 822 __ mov(out, ShifterOperand(0), CC); 823 824 if (kPoisonHeapReferences && type == Primitive::kPrimNot) { 825 codegen->GetAssembler()->UnpoisonHeapReference(value_lo); 826 codegen->GetAssembler()->UnpoisonHeapReference(expected_lo); 827 } 828} 829 830void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) { 831 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke); 832} 833void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) { 834 CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke); 835} 836void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) { 837 GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_); 838} 839void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) { 840 GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); 841} 842 843void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) { 844 LocationSummary* locations = new (arena_) LocationSummary(invoke, 845 LocationSummary::kCallOnSlowPath, 846 kIntrinsified); 847 locations->SetInAt(0, Location::RequiresRegister()); 848 locations->SetInAt(1, Location::RequiresRegister()); 849 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 850 851 locations->AddTemp(Location::RequiresRegister()); 852 locations->AddTemp(Location::RequiresRegister()); 853} 854 855void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) { 856 ArmAssembler* assembler = GetAssembler(); 857 LocationSummary* locations = invoke->GetLocations(); 858 859 // Location of reference to data array 860 const MemberOffset value_offset = mirror::String::ValueOffset(); 861 // Location of count 862 const MemberOffset count_offset = mirror::String::CountOffset(); 863 864 Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer. 865 Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character. 866 Register out = locations->Out().AsRegister<Register>(); // Result character. 867 868 Register temp = locations->GetTemp(0).AsRegister<Register>(); 869 Register array_temp = locations->GetTemp(1).AsRegister<Register>(); 870 871 // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth 872 // the cost. 873 // TODO: For simplicity, the index parameter is requested in a register, so different from Quick 874 // we will not optimize the code for constants (which would save a register). 875 876 SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); 877 codegen_->AddSlowPath(slow_path); 878 879 __ ldr(temp, Address(obj, count_offset.Int32Value())); // temp = str.length. 880 codegen_->MaybeRecordImplicitNullCheck(invoke); 881 __ cmp(idx, ShifterOperand(temp)); 882 __ b(slow_path->GetEntryLabel(), CS); 883 884 __ add(array_temp, obj, ShifterOperand(value_offset.Int32Value())); // array_temp := str.value. 885 886 // Load the value. 887 __ ldrh(out, Address(array_temp, idx, LSL, 1)); // out := array_temp[idx]. 888 889 __ Bind(slow_path->GetExitLabel()); 890} 891 892void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) { 893 // The inputs plus one temp. 894 LocationSummary* locations = new (arena_) LocationSummary(invoke, 895 LocationSummary::kCall, 896 kIntrinsified); 897 InvokeRuntimeCallingConvention calling_convention; 898 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 899 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 900 locations->SetOut(Location::RegisterLocation(R0)); 901} 902 903void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) { 904 ArmAssembler* assembler = GetAssembler(); 905 LocationSummary* locations = invoke->GetLocations(); 906 907 // Note that the null check must have been done earlier. 908 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); 909 910 Register argument = locations->InAt(1).AsRegister<Register>(); 911 __ cmp(argument, ShifterOperand(0)); 912 SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); 913 codegen_->AddSlowPath(slow_path); 914 __ b(slow_path->GetEntryLabel(), EQ); 915 916 __ LoadFromOffset( 917 kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pStringCompareTo).Int32Value()); 918 __ blx(LR); 919 __ Bind(slow_path->GetExitLabel()); 920} 921 922void IntrinsicLocationsBuilderARM::VisitStringEquals(HInvoke* invoke) { 923 LocationSummary* locations = new (arena_) LocationSummary(invoke, 924 LocationSummary::kNoCall, 925 kIntrinsified); 926 InvokeRuntimeCallingConvention calling_convention; 927 locations->SetInAt(0, Location::RequiresRegister()); 928 locations->SetInAt(1, Location::RequiresRegister()); 929 // Temporary registers to store lengths of strings and for calculations. 930 // Using instruction cbz requires a low register, so explicitly set a temp to be R0. 931 locations->AddTemp(Location::RegisterLocation(R0)); 932 locations->AddTemp(Location::RequiresRegister()); 933 locations->AddTemp(Location::RequiresRegister()); 934 935 locations->SetOut(Location::RequiresRegister()); 936} 937 938void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) { 939 ArmAssembler* assembler = GetAssembler(); 940 LocationSummary* locations = invoke->GetLocations(); 941 942 Register str = locations->InAt(0).AsRegister<Register>(); 943 Register arg = locations->InAt(1).AsRegister<Register>(); 944 Register out = locations->Out().AsRegister<Register>(); 945 946 Register temp = locations->GetTemp(0).AsRegister<Register>(); 947 Register temp1 = locations->GetTemp(1).AsRegister<Register>(); 948 Register temp2 = locations->GetTemp(2).AsRegister<Register>(); 949 950 Label loop; 951 Label end; 952 Label return_true; 953 Label return_false; 954 955 // Get offsets of count, value, and class fields within a string object. 956 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); 957 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); 958 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value(); 959 960 // Note that the null check must have been done earlier. 961 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); 962 963 // Check if input is null, return false if it is. 964 __ CompareAndBranchIfZero(arg, &return_false); 965 966 // Instanceof check for the argument by comparing class fields. 967 // All string objects must have the same type since String cannot be subclassed. 968 // Receiver must be a string object, so its class field is equal to all strings' class fields. 969 // If the argument is a string object, its class field must be equal to receiver's class field. 970 __ ldr(temp, Address(str, class_offset)); 971 __ ldr(temp1, Address(arg, class_offset)); 972 __ cmp(temp, ShifterOperand(temp1)); 973 __ b(&return_false, NE); 974 975 // Load lengths of this and argument strings. 976 __ ldr(temp, Address(str, count_offset)); 977 __ ldr(temp1, Address(arg, count_offset)); 978 // Check if lengths are equal, return false if they're not. 979 __ cmp(temp, ShifterOperand(temp1)); 980 __ b(&return_false, NE); 981 // Return true if both strings are empty. 982 __ cbz(temp, &return_true); 983 984 // Reference equality check, return true if same reference. 985 __ cmp(str, ShifterOperand(arg)); 986 __ b(&return_true, EQ); 987 988 // Assertions that must hold in order to compare strings 2 characters at a time. 989 DCHECK_ALIGNED(value_offset, 4); 990 static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded"); 991 992 // temp cannot overflow because we cannot allocate a String object with size 4GiB or greater. 993 __ add(temp, temp, ShifterOperand(temp)); 994 __ LoadImmediate(temp1, value_offset); 995 __ add(temp, temp, ShifterOperand(value_offset)); 996 997 // Loop to compare strings 2 characters at a time starting at the front of the string. 998 // Ok to do this because strings with an odd length are zero-padded. 999 __ Bind(&loop); 1000 __ ldr(out, Address(str, temp1)); 1001 __ ldr(temp2, Address(arg, temp1)); 1002 __ cmp(out, ShifterOperand(temp2)); 1003 __ b(&return_false, NE); 1004 __ add(temp1, temp1, ShifterOperand(sizeof(uint32_t))); 1005 __ cmp(temp1, ShifterOperand(temp)); 1006 __ b(&loop, LO); 1007 1008 // Return true and exit the function. 1009 // If loop does not result in returning false, we return true. 1010 __ Bind(&return_true); 1011 __ LoadImmediate(out, 1); 1012 __ b(&end); 1013 1014 // Return false and exit the function. 1015 __ Bind(&return_false); 1016 __ LoadImmediate(out, 0); 1017 __ Bind(&end); 1018} 1019 1020static void GenerateVisitStringIndexOf(HInvoke* invoke, 1021 ArmAssembler* assembler, 1022 CodeGeneratorARM* codegen, 1023 ArenaAllocator* allocator, 1024 bool start_at_zero) { 1025 LocationSummary* locations = invoke->GetLocations(); 1026 Register tmp_reg = locations->GetTemp(0).AsRegister<Register>(); 1027 1028 // Note that the null check must have been done earlier. 1029 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); 1030 1031 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically, 1032 // or directly dispatch if we have a constant. 1033 SlowPathCodeARM* slow_path = nullptr; 1034 if (invoke->InputAt(1)->IsIntConstant()) { 1035 if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > 1036 std::numeric_limits<uint16_t>::max()) { 1037 // Always needs the slow-path. We could directly dispatch to it, but this case should be 1038 // rare, so for simplicity just put the full slow-path down and branch unconditionally. 1039 slow_path = new (allocator) IntrinsicSlowPathARM(invoke); 1040 codegen->AddSlowPath(slow_path); 1041 __ b(slow_path->GetEntryLabel()); 1042 __ Bind(slow_path->GetExitLabel()); 1043 return; 1044 } 1045 } else { 1046 Register char_reg = locations->InAt(1).AsRegister<Register>(); 1047 __ LoadImmediate(tmp_reg, std::numeric_limits<uint16_t>::max()); 1048 __ cmp(char_reg, ShifterOperand(tmp_reg)); 1049 slow_path = new (allocator) IntrinsicSlowPathARM(invoke); 1050 codegen->AddSlowPath(slow_path); 1051 __ b(slow_path->GetEntryLabel(), HI); 1052 } 1053 1054 if (start_at_zero) { 1055 DCHECK_EQ(tmp_reg, R2); 1056 // Start-index = 0. 1057 __ LoadImmediate(tmp_reg, 0); 1058 } 1059 1060 __ LoadFromOffset(kLoadWord, LR, TR, 1061 QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pIndexOf).Int32Value()); 1062 __ blx(LR); 1063 1064 if (slow_path != nullptr) { 1065 __ Bind(slow_path->GetExitLabel()); 1066 } 1067} 1068 1069void IntrinsicLocationsBuilderARM::VisitStringIndexOf(HInvoke* invoke) { 1070 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1071 LocationSummary::kCall, 1072 kIntrinsified); 1073 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's 1074 // best to align the inputs accordingly. 1075 InvokeRuntimeCallingConvention calling_convention; 1076 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 1077 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 1078 locations->SetOut(Location::RegisterLocation(R0)); 1079 1080 // Need a temp for slow-path codepoint compare, and need to send start-index=0. 1081 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 1082} 1083 1084void IntrinsicCodeGeneratorARM::VisitStringIndexOf(HInvoke* invoke) { 1085 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), true); 1086} 1087 1088void IntrinsicLocationsBuilderARM::VisitStringIndexOfAfter(HInvoke* invoke) { 1089 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1090 LocationSummary::kCall, 1091 kIntrinsified); 1092 // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's 1093 // best to align the inputs accordingly. 1094 InvokeRuntimeCallingConvention calling_convention; 1095 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 1096 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 1097 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 1098 locations->SetOut(Location::RegisterLocation(R0)); 1099 1100 // Need a temp for slow-path codepoint compare. 1101 locations->AddTemp(Location::RequiresRegister()); 1102} 1103 1104void IntrinsicCodeGeneratorARM::VisitStringIndexOfAfter(HInvoke* invoke) { 1105 GenerateVisitStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), false); 1106} 1107 1108void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) { 1109 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1110 LocationSummary::kCall, 1111 kIntrinsified); 1112 InvokeRuntimeCallingConvention calling_convention; 1113 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 1114 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 1115 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 1116 locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); 1117 locations->SetOut(Location::RegisterLocation(R0)); 1118} 1119 1120void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) { 1121 ArmAssembler* assembler = GetAssembler(); 1122 LocationSummary* locations = invoke->GetLocations(); 1123 1124 Register byte_array = locations->InAt(0).AsRegister<Register>(); 1125 __ cmp(byte_array, ShifterOperand(0)); 1126 SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); 1127 codegen_->AddSlowPath(slow_path); 1128 __ b(slow_path->GetEntryLabel(), EQ); 1129 1130 __ LoadFromOffset( 1131 kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromBytes).Int32Value()); 1132 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1133 __ blx(LR); 1134 __ Bind(slow_path->GetExitLabel()); 1135} 1136 1137void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke) { 1138 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1139 LocationSummary::kCall, 1140 kIntrinsified); 1141 InvokeRuntimeCallingConvention calling_convention; 1142 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 1143 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 1144 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 1145 locations->SetOut(Location::RegisterLocation(R0)); 1146} 1147 1148void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) { 1149 ArmAssembler* assembler = GetAssembler(); 1150 1151 __ LoadFromOffset( 1152 kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromChars).Int32Value()); 1153 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1154 __ blx(LR); 1155} 1156 1157void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) { 1158 LocationSummary* locations = new (arena_) LocationSummary(invoke, 1159 LocationSummary::kCall, 1160 kIntrinsified); 1161 InvokeRuntimeCallingConvention calling_convention; 1162 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 1163 locations->SetOut(Location::RegisterLocation(R0)); 1164} 1165 1166void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) { 1167 ArmAssembler* assembler = GetAssembler(); 1168 LocationSummary* locations = invoke->GetLocations(); 1169 1170 Register string_to_copy = locations->InAt(0).AsRegister<Register>(); 1171 __ cmp(string_to_copy, ShifterOperand(0)); 1172 SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); 1173 codegen_->AddSlowPath(slow_path); 1174 __ b(slow_path->GetEntryLabel(), EQ); 1175 1176 __ LoadFromOffset(kLoadWord, 1177 LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromString).Int32Value()); 1178 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1179 __ blx(LR); 1180 __ Bind(slow_path->GetExitLabel()); 1181} 1182 1183// Unimplemented intrinsics. 1184 1185#define UNIMPLEMENTED_INTRINSIC(Name) \ 1186void IntrinsicLocationsBuilderARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ 1187} \ 1188void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ 1189} 1190 1191UNIMPLEMENTED_INTRINSIC(IntegerReverse) 1192UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes) 1193UNIMPLEMENTED_INTRINSIC(LongReverse) 1194UNIMPLEMENTED_INTRINSIC(LongReverseBytes) 1195UNIMPLEMENTED_INTRINSIC(ShortReverseBytes) 1196UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble) 1197UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat) 1198UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble) 1199UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat) 1200UNIMPLEMENTED_INTRINSIC(MathMinLongLong) 1201UNIMPLEMENTED_INTRINSIC(MathMaxLongLong) 1202UNIMPLEMENTED_INTRINSIC(MathCeil) // Could be done by changing rounding mode, maybe? 1203UNIMPLEMENTED_INTRINSIC(MathFloor) // Could be done by changing rounding mode, maybe? 1204UNIMPLEMENTED_INTRINSIC(MathRint) 1205UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding mode, maybe? 1206UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe? 1207UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. 1208UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) 1209UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) 1210UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) 1211 1212#undef UNIMPLEMENTED_INTRINSIC 1213 1214#undef __ 1215 1216} // namespace arm 1217} // namespace art 1218