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