1 // Copyright 2013 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/v8.h" 6 7 #if V8_TARGET_ARCH_ARM64 8 9 #include "src/bootstrapper.h" 10 #include "src/code-stubs.h" 11 #include "src/codegen.h" 12 #include "src/ic/handler-compiler.h" 13 #include "src/ic/ic.h" 14 #include "src/isolate.h" 15 #include "src/jsregexp.h" 16 #include "src/regexp-macro-assembler.h" 17 #include "src/runtime.h" 18 19 namespace v8 { 20 namespace internal { 21 22 23 static void InitializeArrayConstructorDescriptor( 24 Isolate* isolate, CodeStubDescriptor* descriptor, 25 int constant_stack_parameter_count) { 26 // cp: context 27 // x1: function 28 // x2: allocation site with elements kind 29 // x0: number of arguments to the constructor function 30 Address deopt_handler = Runtime::FunctionForId( 31 Runtime::kArrayConstructor)->entry; 32 33 if (constant_stack_parameter_count == 0) { 34 descriptor->Initialize(deopt_handler, constant_stack_parameter_count, 35 JS_FUNCTION_STUB_MODE); 36 } else { 37 descriptor->Initialize(x0, deopt_handler, constant_stack_parameter_count, 38 JS_FUNCTION_STUB_MODE, PASS_ARGUMENTS); 39 } 40 } 41 42 43 void ArrayNoArgumentConstructorStub::InitializeDescriptor( 44 CodeStubDescriptor* descriptor) { 45 InitializeArrayConstructorDescriptor(isolate(), descriptor, 0); 46 } 47 48 49 void ArraySingleArgumentConstructorStub::InitializeDescriptor( 50 CodeStubDescriptor* descriptor) { 51 InitializeArrayConstructorDescriptor(isolate(), descriptor, 1); 52 } 53 54 55 void ArrayNArgumentsConstructorStub::InitializeDescriptor( 56 CodeStubDescriptor* descriptor) { 57 InitializeArrayConstructorDescriptor(isolate(), descriptor, -1); 58 } 59 60 61 static void InitializeInternalArrayConstructorDescriptor( 62 Isolate* isolate, CodeStubDescriptor* descriptor, 63 int constant_stack_parameter_count) { 64 Address deopt_handler = Runtime::FunctionForId( 65 Runtime::kInternalArrayConstructor)->entry; 66 67 if (constant_stack_parameter_count == 0) { 68 descriptor->Initialize(deopt_handler, constant_stack_parameter_count, 69 JS_FUNCTION_STUB_MODE); 70 } else { 71 descriptor->Initialize(x0, deopt_handler, constant_stack_parameter_count, 72 JS_FUNCTION_STUB_MODE, PASS_ARGUMENTS); 73 } 74 } 75 76 77 void InternalArrayNoArgumentConstructorStub::InitializeDescriptor( 78 CodeStubDescriptor* descriptor) { 79 InitializeInternalArrayConstructorDescriptor(isolate(), descriptor, 0); 80 } 81 82 83 void InternalArraySingleArgumentConstructorStub::InitializeDescriptor( 84 CodeStubDescriptor* descriptor) { 85 InitializeInternalArrayConstructorDescriptor(isolate(), descriptor, 1); 86 } 87 88 89 void InternalArrayNArgumentsConstructorStub::InitializeDescriptor( 90 CodeStubDescriptor* descriptor) { 91 InitializeInternalArrayConstructorDescriptor(isolate(), descriptor, -1); 92 } 93 94 95 #define __ ACCESS_MASM(masm) 96 97 98 void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm, 99 ExternalReference miss) { 100 // Update the static counter each time a new code stub is generated. 101 isolate()->counters()->code_stubs()->Increment(); 102 103 CallInterfaceDescriptor descriptor = GetCallInterfaceDescriptor(); 104 int param_count = descriptor.GetEnvironmentParameterCount(); 105 { 106 // Call the runtime system in a fresh internal frame. 107 FrameScope scope(masm, StackFrame::INTERNAL); 108 DCHECK((param_count == 0) || 109 x0.Is(descriptor.GetEnvironmentParameterRegister(param_count - 1))); 110 111 // Push arguments 112 MacroAssembler::PushPopQueue queue(masm); 113 for (int i = 0; i < param_count; ++i) { 114 queue.Queue(descriptor.GetEnvironmentParameterRegister(i)); 115 } 116 queue.PushQueued(); 117 118 __ CallExternalReference(miss, param_count); 119 } 120 121 __ Ret(); 122 } 123 124 125 void DoubleToIStub::Generate(MacroAssembler* masm) { 126 Label done; 127 Register input = source(); 128 Register result = destination(); 129 DCHECK(is_truncating()); 130 131 DCHECK(result.Is64Bits()); 132 DCHECK(jssp.Is(masm->StackPointer())); 133 134 int double_offset = offset(); 135 136 DoubleRegister double_scratch = d0; // only used if !skip_fastpath() 137 Register scratch1 = GetAllocatableRegisterThatIsNotOneOf(input, result); 138 Register scratch2 = 139 GetAllocatableRegisterThatIsNotOneOf(input, result, scratch1); 140 141 __ Push(scratch1, scratch2); 142 // Account for saved regs if input is jssp. 143 if (input.is(jssp)) double_offset += 2 * kPointerSize; 144 145 if (!skip_fastpath()) { 146 __ Push(double_scratch); 147 if (input.is(jssp)) double_offset += 1 * kDoubleSize; 148 __ Ldr(double_scratch, MemOperand(input, double_offset)); 149 // Try to convert with a FPU convert instruction. This handles all 150 // non-saturating cases. 151 __ TryConvertDoubleToInt64(result, double_scratch, &done); 152 __ Fmov(result, double_scratch); 153 } else { 154 __ Ldr(result, MemOperand(input, double_offset)); 155 } 156 157 // If we reach here we need to manually convert the input to an int32. 158 159 // Extract the exponent. 160 Register exponent = scratch1; 161 __ Ubfx(exponent, result, HeapNumber::kMantissaBits, 162 HeapNumber::kExponentBits); 163 164 // It the exponent is >= 84 (kMantissaBits + 32), the result is always 0 since 165 // the mantissa gets shifted completely out of the int32_t result. 166 __ Cmp(exponent, HeapNumber::kExponentBias + HeapNumber::kMantissaBits + 32); 167 __ CzeroX(result, ge); 168 __ B(ge, &done); 169 170 // The Fcvtzs sequence handles all cases except where the conversion causes 171 // signed overflow in the int64_t target. Since we've already handled 172 // exponents >= 84, we can guarantee that 63 <= exponent < 84. 173 174 if (masm->emit_debug_code()) { 175 __ Cmp(exponent, HeapNumber::kExponentBias + 63); 176 // Exponents less than this should have been handled by the Fcvt case. 177 __ Check(ge, kUnexpectedValue); 178 } 179 180 // Isolate the mantissa bits, and set the implicit '1'. 181 Register mantissa = scratch2; 182 __ Ubfx(mantissa, result, 0, HeapNumber::kMantissaBits); 183 __ Orr(mantissa, mantissa, 1UL << HeapNumber::kMantissaBits); 184 185 // Negate the mantissa if necessary. 186 __ Tst(result, kXSignMask); 187 __ Cneg(mantissa, mantissa, ne); 188 189 // Shift the mantissa bits in the correct place. We know that we have to shift 190 // it left here, because exponent >= 63 >= kMantissaBits. 191 __ Sub(exponent, exponent, 192 HeapNumber::kExponentBias + HeapNumber::kMantissaBits); 193 __ Lsl(result, mantissa, exponent); 194 195 __ Bind(&done); 196 if (!skip_fastpath()) { 197 __ Pop(double_scratch); 198 } 199 __ Pop(scratch2, scratch1); 200 __ Ret(); 201 } 202 203 204 // See call site for description. 205 static void EmitIdenticalObjectComparison(MacroAssembler* masm, 206 Register left, 207 Register right, 208 Register scratch, 209 FPRegister double_scratch, 210 Label* slow, 211 Condition cond) { 212 DCHECK(!AreAliased(left, right, scratch)); 213 Label not_identical, return_equal, heap_number; 214 Register result = x0; 215 216 __ Cmp(right, left); 217 __ B(ne, ¬_identical); 218 219 // Test for NaN. Sadly, we can't just compare to factory::nan_value(), 220 // so we do the second best thing - test it ourselves. 221 // They are both equal and they are not both Smis so both of them are not 222 // Smis. If it's not a heap number, then return equal. 223 if ((cond == lt) || (cond == gt)) { 224 __ JumpIfObjectType(right, scratch, scratch, FIRST_SPEC_OBJECT_TYPE, slow, 225 ge); 226 } else if (cond == eq) { 227 __ JumpIfHeapNumber(right, &heap_number); 228 } else { 229 Register right_type = scratch; 230 __ JumpIfObjectType(right, right_type, right_type, HEAP_NUMBER_TYPE, 231 &heap_number); 232 // Comparing JS objects with <=, >= is complicated. 233 __ Cmp(right_type, FIRST_SPEC_OBJECT_TYPE); 234 __ B(ge, slow); 235 // Normally here we fall through to return_equal, but undefined is 236 // special: (undefined == undefined) == true, but 237 // (undefined <= undefined) == false! See ECMAScript 11.8.5. 238 if ((cond == le) || (cond == ge)) { 239 __ Cmp(right_type, ODDBALL_TYPE); 240 __ B(ne, &return_equal); 241 __ JumpIfNotRoot(right, Heap::kUndefinedValueRootIndex, &return_equal); 242 if (cond == le) { 243 // undefined <= undefined should fail. 244 __ Mov(result, GREATER); 245 } else { 246 // undefined >= undefined should fail. 247 __ Mov(result, LESS); 248 } 249 __ Ret(); 250 } 251 } 252 253 __ Bind(&return_equal); 254 if (cond == lt) { 255 __ Mov(result, GREATER); // Things aren't less than themselves. 256 } else if (cond == gt) { 257 __ Mov(result, LESS); // Things aren't greater than themselves. 258 } else { 259 __ Mov(result, EQUAL); // Things are <=, >=, ==, === themselves. 260 } 261 __ Ret(); 262 263 // Cases lt and gt have been handled earlier, and case ne is never seen, as 264 // it is handled in the parser (see Parser::ParseBinaryExpression). We are 265 // only concerned with cases ge, le and eq here. 266 if ((cond != lt) && (cond != gt)) { 267 DCHECK((cond == ge) || (cond == le) || (cond == eq)); 268 __ Bind(&heap_number); 269 // Left and right are identical pointers to a heap number object. Return 270 // non-equal if the heap number is a NaN, and equal otherwise. Comparing 271 // the number to itself will set the overflow flag iff the number is NaN. 272 __ Ldr(double_scratch, FieldMemOperand(right, HeapNumber::kValueOffset)); 273 __ Fcmp(double_scratch, double_scratch); 274 __ B(vc, &return_equal); // Not NaN, so treat as normal heap number. 275 276 if (cond == le) { 277 __ Mov(result, GREATER); 278 } else { 279 __ Mov(result, LESS); 280 } 281 __ Ret(); 282 } 283 284 // No fall through here. 285 if (FLAG_debug_code) { 286 __ Unreachable(); 287 } 288 289 __ Bind(¬_identical); 290 } 291 292 293 // See call site for description. 294 static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm, 295 Register left, 296 Register right, 297 Register left_type, 298 Register right_type, 299 Register scratch) { 300 DCHECK(!AreAliased(left, right, left_type, right_type, scratch)); 301 302 if (masm->emit_debug_code()) { 303 // We assume that the arguments are not identical. 304 __ Cmp(left, right); 305 __ Assert(ne, kExpectedNonIdenticalObjects); 306 } 307 308 // If either operand is a JS object or an oddball value, then they are not 309 // equal since their pointers are different. 310 // There is no test for undetectability in strict equality. 311 STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE); 312 Label right_non_object; 313 314 __ Cmp(right_type, FIRST_SPEC_OBJECT_TYPE); 315 __ B(lt, &right_non_object); 316 317 // Return non-zero - x0 already contains a non-zero pointer. 318 DCHECK(left.is(x0) || right.is(x0)); 319 Label return_not_equal; 320 __ Bind(&return_not_equal); 321 __ Ret(); 322 323 __ Bind(&right_non_object); 324 325 // Check for oddballs: true, false, null, undefined. 326 __ Cmp(right_type, ODDBALL_TYPE); 327 328 // If right is not ODDBALL, test left. Otherwise, set eq condition. 329 __ Ccmp(left_type, ODDBALL_TYPE, ZFlag, ne); 330 331 // If right or left is not ODDBALL, test left >= FIRST_SPEC_OBJECT_TYPE. 332 // Otherwise, right or left is ODDBALL, so set a ge condition. 333 __ Ccmp(left_type, FIRST_SPEC_OBJECT_TYPE, NVFlag, ne); 334 335 __ B(ge, &return_not_equal); 336 337 // Internalized strings are unique, so they can only be equal if they are the 338 // same object. We have already tested that case, so if left and right are 339 // both internalized strings, they cannot be equal. 340 STATIC_ASSERT((kInternalizedTag == 0) && (kStringTag == 0)); 341 __ Orr(scratch, left_type, right_type); 342 __ TestAndBranchIfAllClear( 343 scratch, kIsNotStringMask | kIsNotInternalizedMask, &return_not_equal); 344 } 345 346 347 // See call site for description. 348 static void EmitSmiNonsmiComparison(MacroAssembler* masm, 349 Register left, 350 Register right, 351 FPRegister left_d, 352 FPRegister right_d, 353 Label* slow, 354 bool strict) { 355 DCHECK(!AreAliased(left_d, right_d)); 356 DCHECK((left.is(x0) && right.is(x1)) || 357 (right.is(x0) && left.is(x1))); 358 Register result = x0; 359 360 Label right_is_smi, done; 361 __ JumpIfSmi(right, &right_is_smi); 362 363 // Left is the smi. Check whether right is a heap number. 364 if (strict) { 365 // If right is not a number and left is a smi, then strict equality cannot 366 // succeed. Return non-equal. 367 Label is_heap_number; 368 __ JumpIfHeapNumber(right, &is_heap_number); 369 // Register right is a non-zero pointer, which is a valid NOT_EQUAL result. 370 if (!right.is(result)) { 371 __ Mov(result, NOT_EQUAL); 372 } 373 __ Ret(); 374 __ Bind(&is_heap_number); 375 } else { 376 // Smi compared non-strictly with a non-smi, non-heap-number. Call the 377 // runtime. 378 __ JumpIfNotHeapNumber(right, slow); 379 } 380 381 // Left is the smi. Right is a heap number. Load right value into right_d, and 382 // convert left smi into double in left_d. 383 __ Ldr(right_d, FieldMemOperand(right, HeapNumber::kValueOffset)); 384 __ SmiUntagToDouble(left_d, left); 385 __ B(&done); 386 387 __ Bind(&right_is_smi); 388 // Right is a smi. Check whether the non-smi left is a heap number. 389 if (strict) { 390 // If left is not a number and right is a smi then strict equality cannot 391 // succeed. Return non-equal. 392 Label is_heap_number; 393 __ JumpIfHeapNumber(left, &is_heap_number); 394 // Register left is a non-zero pointer, which is a valid NOT_EQUAL result. 395 if (!left.is(result)) { 396 __ Mov(result, NOT_EQUAL); 397 } 398 __ Ret(); 399 __ Bind(&is_heap_number); 400 } else { 401 // Smi compared non-strictly with a non-smi, non-heap-number. Call the 402 // runtime. 403 __ JumpIfNotHeapNumber(left, slow); 404 } 405 406 // Right is the smi. Left is a heap number. Load left value into left_d, and 407 // convert right smi into double in right_d. 408 __ Ldr(left_d, FieldMemOperand(left, HeapNumber::kValueOffset)); 409 __ SmiUntagToDouble(right_d, right); 410 411 // Fall through to both_loaded_as_doubles. 412 __ Bind(&done); 413 } 414 415 416 // Fast negative check for internalized-to-internalized equality. 417 // See call site for description. 418 static void EmitCheckForInternalizedStringsOrObjects(MacroAssembler* masm, 419 Register left, 420 Register right, 421 Register left_map, 422 Register right_map, 423 Register left_type, 424 Register right_type, 425 Label* possible_strings, 426 Label* not_both_strings) { 427 DCHECK(!AreAliased(left, right, left_map, right_map, left_type, right_type)); 428 Register result = x0; 429 430 Label object_test; 431 STATIC_ASSERT((kInternalizedTag == 0) && (kStringTag == 0)); 432 // TODO(all): reexamine this branch sequence for optimisation wrt branch 433 // prediction. 434 __ Tbnz(right_type, MaskToBit(kIsNotStringMask), &object_test); 435 __ Tbnz(right_type, MaskToBit(kIsNotInternalizedMask), possible_strings); 436 __ Tbnz(left_type, MaskToBit(kIsNotStringMask), not_both_strings); 437 __ Tbnz(left_type, MaskToBit(kIsNotInternalizedMask), possible_strings); 438 439 // Both are internalized. We already checked that they weren't the same 440 // pointer, so they are not equal. 441 __ Mov(result, NOT_EQUAL); 442 __ Ret(); 443 444 __ Bind(&object_test); 445 446 __ Cmp(right_type, FIRST_SPEC_OBJECT_TYPE); 447 448 // If right >= FIRST_SPEC_OBJECT_TYPE, test left. 449 // Otherwise, right < FIRST_SPEC_OBJECT_TYPE, so set lt condition. 450 __ Ccmp(left_type, FIRST_SPEC_OBJECT_TYPE, NFlag, ge); 451 452 __ B(lt, not_both_strings); 453 454 // If both objects are undetectable, they are equal. Otherwise, they are not 455 // equal, since they are different objects and an object is not equal to 456 // undefined. 457 458 // Returning here, so we can corrupt right_type and left_type. 459 Register right_bitfield = right_type; 460 Register left_bitfield = left_type; 461 __ Ldrb(right_bitfield, FieldMemOperand(right_map, Map::kBitFieldOffset)); 462 __ Ldrb(left_bitfield, FieldMemOperand(left_map, Map::kBitFieldOffset)); 463 __ And(result, right_bitfield, left_bitfield); 464 __ And(result, result, 1 << Map::kIsUndetectable); 465 __ Eor(result, result, 1 << Map::kIsUndetectable); 466 __ Ret(); 467 } 468 469 470 static void CompareICStub_CheckInputType(MacroAssembler* masm, Register input, 471 CompareICState::State expected, 472 Label* fail) { 473 Label ok; 474 if (expected == CompareICState::SMI) { 475 __ JumpIfNotSmi(input, fail); 476 } else if (expected == CompareICState::NUMBER) { 477 __ JumpIfSmi(input, &ok); 478 __ JumpIfNotHeapNumber(input, fail); 479 } 480 // We could be strict about internalized/non-internalized here, but as long as 481 // hydrogen doesn't care, the stub doesn't have to care either. 482 __ Bind(&ok); 483 } 484 485 486 void CompareICStub::GenerateGeneric(MacroAssembler* masm) { 487 Register lhs = x1; 488 Register rhs = x0; 489 Register result = x0; 490 Condition cond = GetCondition(); 491 492 Label miss; 493 CompareICStub_CheckInputType(masm, lhs, left(), &miss); 494 CompareICStub_CheckInputType(masm, rhs, right(), &miss); 495 496 Label slow; // Call builtin. 497 Label not_smis, both_loaded_as_doubles; 498 Label not_two_smis, smi_done; 499 __ JumpIfEitherNotSmi(lhs, rhs, ¬_two_smis); 500 __ SmiUntag(lhs); 501 __ Sub(result, lhs, Operand::UntagSmi(rhs)); 502 __ Ret(); 503 504 __ Bind(¬_two_smis); 505 506 // NOTICE! This code is only reached after a smi-fast-case check, so it is 507 // certain that at least one operand isn't a smi. 508 509 // Handle the case where the objects are identical. Either returns the answer 510 // or goes to slow. Only falls through if the objects were not identical. 511 EmitIdenticalObjectComparison(masm, lhs, rhs, x10, d0, &slow, cond); 512 513 // If either is a smi (we know that at least one is not a smi), then they can 514 // only be strictly equal if the other is a HeapNumber. 515 __ JumpIfBothNotSmi(lhs, rhs, ¬_smis); 516 517 // Exactly one operand is a smi. EmitSmiNonsmiComparison generates code that 518 // can: 519 // 1) Return the answer. 520 // 2) Branch to the slow case. 521 // 3) Fall through to both_loaded_as_doubles. 522 // In case 3, we have found out that we were dealing with a number-number 523 // comparison. The double values of the numbers have been loaded, right into 524 // rhs_d, left into lhs_d. 525 FPRegister rhs_d = d0; 526 FPRegister lhs_d = d1; 527 EmitSmiNonsmiComparison(masm, lhs, rhs, lhs_d, rhs_d, &slow, strict()); 528 529 __ Bind(&both_loaded_as_doubles); 530 // The arguments have been converted to doubles and stored in rhs_d and 531 // lhs_d. 532 Label nan; 533 __ Fcmp(lhs_d, rhs_d); 534 __ B(vs, &nan); // Overflow flag set if either is NaN. 535 STATIC_ASSERT((LESS == -1) && (EQUAL == 0) && (GREATER == 1)); 536 __ Cset(result, gt); // gt => 1, otherwise (lt, eq) => 0 (EQUAL). 537 __ Csinv(result, result, xzr, ge); // lt => -1, gt => 1, eq => 0. 538 __ Ret(); 539 540 __ Bind(&nan); 541 // Left and/or right is a NaN. Load the result register with whatever makes 542 // the comparison fail, since comparisons with NaN always fail (except ne, 543 // which is filtered out at a higher level.) 544 DCHECK(cond != ne); 545 if ((cond == lt) || (cond == le)) { 546 __ Mov(result, GREATER); 547 } else { 548 __ Mov(result, LESS); 549 } 550 __ Ret(); 551 552 __ Bind(¬_smis); 553 // At this point we know we are dealing with two different objects, and 554 // neither of them is a smi. The objects are in rhs_ and lhs_. 555 556 // Load the maps and types of the objects. 557 Register rhs_map = x10; 558 Register rhs_type = x11; 559 Register lhs_map = x12; 560 Register lhs_type = x13; 561 __ Ldr(rhs_map, FieldMemOperand(rhs, HeapObject::kMapOffset)); 562 __ Ldr(lhs_map, FieldMemOperand(lhs, HeapObject::kMapOffset)); 563 __ Ldrb(rhs_type, FieldMemOperand(rhs_map, Map::kInstanceTypeOffset)); 564 __ Ldrb(lhs_type, FieldMemOperand(lhs_map, Map::kInstanceTypeOffset)); 565 566 if (strict()) { 567 // This emits a non-equal return sequence for some object types, or falls 568 // through if it was not lucky. 569 EmitStrictTwoHeapObjectCompare(masm, lhs, rhs, lhs_type, rhs_type, x14); 570 } 571 572 Label check_for_internalized_strings; 573 Label flat_string_check; 574 // Check for heap number comparison. Branch to earlier double comparison code 575 // if they are heap numbers, otherwise, branch to internalized string check. 576 __ Cmp(rhs_type, HEAP_NUMBER_TYPE); 577 __ B(ne, &check_for_internalized_strings); 578 __ Cmp(lhs_map, rhs_map); 579 580 // If maps aren't equal, lhs_ and rhs_ are not heap numbers. Branch to flat 581 // string check. 582 __ B(ne, &flat_string_check); 583 584 // Both lhs_ and rhs_ are heap numbers. Load them and branch to the double 585 // comparison code. 586 __ Ldr(lhs_d, FieldMemOperand(lhs, HeapNumber::kValueOffset)); 587 __ Ldr(rhs_d, FieldMemOperand(rhs, HeapNumber::kValueOffset)); 588 __ B(&both_loaded_as_doubles); 589 590 __ Bind(&check_for_internalized_strings); 591 // In the strict case, the EmitStrictTwoHeapObjectCompare already took care 592 // of internalized strings. 593 if ((cond == eq) && !strict()) { 594 // Returns an answer for two internalized strings or two detectable objects. 595 // Otherwise branches to the string case or not both strings case. 596 EmitCheckForInternalizedStringsOrObjects(masm, lhs, rhs, lhs_map, rhs_map, 597 lhs_type, rhs_type, 598 &flat_string_check, &slow); 599 } 600 601 // Check for both being sequential one-byte strings, 602 // and inline if that is the case. 603 __ Bind(&flat_string_check); 604 __ JumpIfBothInstanceTypesAreNotSequentialOneByte(lhs_type, rhs_type, x14, 605 x15, &slow); 606 607 __ IncrementCounter(isolate()->counters()->string_compare_native(), 1, x10, 608 x11); 609 if (cond == eq) { 610 StringHelper::GenerateFlatOneByteStringEquals(masm, lhs, rhs, x10, x11, 611 x12); 612 } else { 613 StringHelper::GenerateCompareFlatOneByteStrings(masm, lhs, rhs, x10, x11, 614 x12, x13); 615 } 616 617 // Never fall through to here. 618 if (FLAG_debug_code) { 619 __ Unreachable(); 620 } 621 622 __ Bind(&slow); 623 624 __ Push(lhs, rhs); 625 // Figure out which native to call and setup the arguments. 626 Builtins::JavaScript native; 627 if (cond == eq) { 628 native = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS; 629 } else { 630 native = Builtins::COMPARE; 631 int ncr; // NaN compare result 632 if ((cond == lt) || (cond == le)) { 633 ncr = GREATER; 634 } else { 635 DCHECK((cond == gt) || (cond == ge)); // remaining cases 636 ncr = LESS; 637 } 638 __ Mov(x10, Smi::FromInt(ncr)); 639 __ Push(x10); 640 } 641 642 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater) 643 // tagged as a small integer. 644 __ InvokeBuiltin(native, JUMP_FUNCTION); 645 646 __ Bind(&miss); 647 GenerateMiss(masm); 648 } 649 650 651 void StoreBufferOverflowStub::Generate(MacroAssembler* masm) { 652 CPURegList saved_regs = kCallerSaved; 653 CPURegList saved_fp_regs = kCallerSavedFP; 654 655 // We don't allow a GC during a store buffer overflow so there is no need to 656 // store the registers in any particular way, but we do have to store and 657 // restore them. 658 659 // We don't care if MacroAssembler scratch registers are corrupted. 660 saved_regs.Remove(*(masm->TmpList())); 661 saved_fp_regs.Remove(*(masm->FPTmpList())); 662 663 __ PushCPURegList(saved_regs); 664 if (save_doubles()) { 665 __ PushCPURegList(saved_fp_regs); 666 } 667 668 AllowExternalCallThatCantCauseGC scope(masm); 669 __ Mov(x0, ExternalReference::isolate_address(isolate())); 670 __ CallCFunction( 671 ExternalReference::store_buffer_overflow_function(isolate()), 1, 0); 672 673 if (save_doubles()) { 674 __ PopCPURegList(saved_fp_regs); 675 } 676 __ PopCPURegList(saved_regs); 677 __ Ret(); 678 } 679 680 681 void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime( 682 Isolate* isolate) { 683 StoreBufferOverflowStub stub1(isolate, kDontSaveFPRegs); 684 stub1.GetCode(); 685 StoreBufferOverflowStub stub2(isolate, kSaveFPRegs); 686 stub2.GetCode(); 687 } 688 689 690 void StoreRegistersStateStub::Generate(MacroAssembler* masm) { 691 MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm); 692 UseScratchRegisterScope temps(masm); 693 Register saved_lr = temps.UnsafeAcquire(to_be_pushed_lr()); 694 Register return_address = temps.AcquireX(); 695 __ Mov(return_address, lr); 696 // Restore lr with the value it had before the call to this stub (the value 697 // which must be pushed). 698 __ Mov(lr, saved_lr); 699 __ PushSafepointRegisters(); 700 __ Ret(return_address); 701 } 702 703 704 void RestoreRegistersStateStub::Generate(MacroAssembler* masm) { 705 MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm); 706 UseScratchRegisterScope temps(masm); 707 Register return_address = temps.AcquireX(); 708 // Preserve the return address (lr will be clobbered by the pop). 709 __ Mov(return_address, lr); 710 __ PopSafepointRegisters(); 711 __ Ret(return_address); 712 } 713 714 715 void MathPowStub::Generate(MacroAssembler* masm) { 716 // Stack on entry: 717 // jssp[0]: Exponent (as a tagged value). 718 // jssp[1]: Base (as a tagged value). 719 // 720 // The (tagged) result will be returned in x0, as a heap number. 721 722 Register result_tagged = x0; 723 Register base_tagged = x10; 724 Register exponent_tagged = MathPowTaggedDescriptor::exponent(); 725 DCHECK(exponent_tagged.is(x11)); 726 Register exponent_integer = MathPowIntegerDescriptor::exponent(); 727 DCHECK(exponent_integer.is(x12)); 728 Register scratch1 = x14; 729 Register scratch0 = x15; 730 Register saved_lr = x19; 731 FPRegister result_double = d0; 732 FPRegister base_double = d0; 733 FPRegister exponent_double = d1; 734 FPRegister base_double_copy = d2; 735 FPRegister scratch1_double = d6; 736 FPRegister scratch0_double = d7; 737 738 // A fast-path for integer exponents. 739 Label exponent_is_smi, exponent_is_integer; 740 // Bail out to runtime. 741 Label call_runtime; 742 // Allocate a heap number for the result, and return it. 743 Label done; 744 745 // Unpack the inputs. 746 if (exponent_type() == ON_STACK) { 747 Label base_is_smi; 748 Label unpack_exponent; 749 750 __ Pop(exponent_tagged, base_tagged); 751 752 __ JumpIfSmi(base_tagged, &base_is_smi); 753 __ JumpIfNotHeapNumber(base_tagged, &call_runtime); 754 // base_tagged is a heap number, so load its double value. 755 __ Ldr(base_double, FieldMemOperand(base_tagged, HeapNumber::kValueOffset)); 756 __ B(&unpack_exponent); 757 __ Bind(&base_is_smi); 758 // base_tagged is a SMI, so untag it and convert it to a double. 759 __ SmiUntagToDouble(base_double, base_tagged); 760 761 __ Bind(&unpack_exponent); 762 // x10 base_tagged The tagged base (input). 763 // x11 exponent_tagged The tagged exponent (input). 764 // d1 base_double The base as a double. 765 __ JumpIfSmi(exponent_tagged, &exponent_is_smi); 766 __ JumpIfNotHeapNumber(exponent_tagged, &call_runtime); 767 // exponent_tagged is a heap number, so load its double value. 768 __ Ldr(exponent_double, 769 FieldMemOperand(exponent_tagged, HeapNumber::kValueOffset)); 770 } else if (exponent_type() == TAGGED) { 771 __ JumpIfSmi(exponent_tagged, &exponent_is_smi); 772 __ Ldr(exponent_double, 773 FieldMemOperand(exponent_tagged, HeapNumber::kValueOffset)); 774 } 775 776 // Handle double (heap number) exponents. 777 if (exponent_type() != INTEGER) { 778 // Detect integer exponents stored as doubles and handle those in the 779 // integer fast-path. 780 __ TryRepresentDoubleAsInt64(exponent_integer, exponent_double, 781 scratch0_double, &exponent_is_integer); 782 783 if (exponent_type() == ON_STACK) { 784 FPRegister half_double = d3; 785 FPRegister minus_half_double = d4; 786 // Detect square root case. Crankshaft detects constant +/-0.5 at compile 787 // time and uses DoMathPowHalf instead. We then skip this check for 788 // non-constant cases of +/-0.5 as these hardly occur. 789 790 __ Fmov(minus_half_double, -0.5); 791 __ Fmov(half_double, 0.5); 792 __ Fcmp(minus_half_double, exponent_double); 793 __ Fccmp(half_double, exponent_double, NZFlag, ne); 794 // Condition flags at this point: 795 // 0.5; nZCv // Identified by eq && pl 796 // -0.5: NZcv // Identified by eq && mi 797 // other: ?z?? // Identified by ne 798 __ B(ne, &call_runtime); 799 800 // The exponent is 0.5 or -0.5. 801 802 // Given that exponent is known to be either 0.5 or -0.5, the following 803 // special cases could apply (according to ECMA-262 15.8.2.13): 804 // 805 // base.isNaN(): The result is NaN. 806 // (base == +INFINITY) || (base == -INFINITY) 807 // exponent == 0.5: The result is +INFINITY. 808 // exponent == -0.5: The result is +0. 809 // (base == +0) || (base == -0) 810 // exponent == 0.5: The result is +0. 811 // exponent == -0.5: The result is +INFINITY. 812 // (base < 0) && base.isFinite(): The result is NaN. 813 // 814 // Fsqrt (and Fdiv for the -0.5 case) can handle all of those except 815 // where base is -INFINITY or -0. 816 817 // Add +0 to base. This has no effect other than turning -0 into +0. 818 __ Fadd(base_double, base_double, fp_zero); 819 // The operation -0+0 results in +0 in all cases except where the 820 // FPCR rounding mode is 'round towards minus infinity' (RM). The 821 // ARM64 simulator does not currently simulate FPCR (where the rounding 822 // mode is set), so test the operation with some debug code. 823 if (masm->emit_debug_code()) { 824 UseScratchRegisterScope temps(masm); 825 Register temp = temps.AcquireX(); 826 __ Fneg(scratch0_double, fp_zero); 827 // Verify that we correctly generated +0.0 and -0.0. 828 // bits(+0.0) = 0x0000000000000000 829 // bits(-0.0) = 0x8000000000000000 830 __ Fmov(temp, fp_zero); 831 __ CheckRegisterIsClear(temp, kCouldNotGenerateZero); 832 __ Fmov(temp, scratch0_double); 833 __ Eor(temp, temp, kDSignMask); 834 __ CheckRegisterIsClear(temp, kCouldNotGenerateNegativeZero); 835 // Check that -0.0 + 0.0 == +0.0. 836 __ Fadd(scratch0_double, scratch0_double, fp_zero); 837 __ Fmov(temp, scratch0_double); 838 __ CheckRegisterIsClear(temp, kExpectedPositiveZero); 839 } 840 841 // If base is -INFINITY, make it +INFINITY. 842 // * Calculate base - base: All infinities will become NaNs since both 843 // -INFINITY+INFINITY and +INFINITY-INFINITY are NaN in ARM64. 844 // * If the result is NaN, calculate abs(base). 845 __ Fsub(scratch0_double, base_double, base_double); 846 __ Fcmp(scratch0_double, 0.0); 847 __ Fabs(scratch1_double, base_double); 848 __ Fcsel(base_double, scratch1_double, base_double, vs); 849 850 // Calculate the square root of base. 851 __ Fsqrt(result_double, base_double); 852 __ Fcmp(exponent_double, 0.0); 853 __ B(ge, &done); // Finish now for exponents of 0.5. 854 // Find the inverse for exponents of -0.5. 855 __ Fmov(scratch0_double, 1.0); 856 __ Fdiv(result_double, scratch0_double, result_double); 857 __ B(&done); 858 } 859 860 { 861 AllowExternalCallThatCantCauseGC scope(masm); 862 __ Mov(saved_lr, lr); 863 __ CallCFunction( 864 ExternalReference::power_double_double_function(isolate()), 865 0, 2); 866 __ Mov(lr, saved_lr); 867 __ B(&done); 868 } 869 870 // Handle SMI exponents. 871 __ Bind(&exponent_is_smi); 872 // x10 base_tagged The tagged base (input). 873 // x11 exponent_tagged The tagged exponent (input). 874 // d1 base_double The base as a double. 875 __ SmiUntag(exponent_integer, exponent_tagged); 876 } 877 878 __ Bind(&exponent_is_integer); 879 // x10 base_tagged The tagged base (input). 880 // x11 exponent_tagged The tagged exponent (input). 881 // x12 exponent_integer The exponent as an integer. 882 // d1 base_double The base as a double. 883 884 // Find abs(exponent). For negative exponents, we can find the inverse later. 885 Register exponent_abs = x13; 886 __ Cmp(exponent_integer, 0); 887 __ Cneg(exponent_abs, exponent_integer, mi); 888 // x13 exponent_abs The value of abs(exponent_integer). 889 890 // Repeatedly multiply to calculate the power. 891 // result = 1.0; 892 // For each bit n (exponent_integer{n}) { 893 // if (exponent_integer{n}) { 894 // result *= base; 895 // } 896 // base *= base; 897 // if (remaining bits in exponent_integer are all zero) { 898 // break; 899 // } 900 // } 901 Label power_loop, power_loop_entry, power_loop_exit; 902 __ Fmov(scratch1_double, base_double); 903 __ Fmov(base_double_copy, base_double); 904 __ Fmov(result_double, 1.0); 905 __ B(&power_loop_entry); 906 907 __ Bind(&power_loop); 908 __ Fmul(scratch1_double, scratch1_double, scratch1_double); 909 __ Lsr(exponent_abs, exponent_abs, 1); 910 __ Cbz(exponent_abs, &power_loop_exit); 911 912 __ Bind(&power_loop_entry); 913 __ Tbz(exponent_abs, 0, &power_loop); 914 __ Fmul(result_double, result_double, scratch1_double); 915 __ B(&power_loop); 916 917 __ Bind(&power_loop_exit); 918 919 // If the exponent was positive, result_double holds the result. 920 __ Tbz(exponent_integer, kXSignBit, &done); 921 922 // The exponent was negative, so find the inverse. 923 __ Fmov(scratch0_double, 1.0); 924 __ Fdiv(result_double, scratch0_double, result_double); 925 // ECMA-262 only requires Math.pow to return an 'implementation-dependent 926 // approximation' of base^exponent. However, mjsunit/math-pow uses Math.pow 927 // to calculate the subnormal value 2^-1074. This method of calculating 928 // negative powers doesn't work because 2^1074 overflows to infinity. To 929 // catch this corner-case, we bail out if the result was 0. (This can only 930 // occur if the divisor is infinity or the base is zero.) 931 __ Fcmp(result_double, 0.0); 932 __ B(&done, ne); 933 934 if (exponent_type() == ON_STACK) { 935 // Bail out to runtime code. 936 __ Bind(&call_runtime); 937 // Put the arguments back on the stack. 938 __ Push(base_tagged, exponent_tagged); 939 __ TailCallRuntime(Runtime::kMathPowRT, 2, 1); 940 941 // Return. 942 __ Bind(&done); 943 __ AllocateHeapNumber(result_tagged, &call_runtime, scratch0, scratch1, 944 result_double); 945 DCHECK(result_tagged.is(x0)); 946 __ IncrementCounter( 947 isolate()->counters()->math_pow(), 1, scratch0, scratch1); 948 __ Ret(); 949 } else { 950 AllowExternalCallThatCantCauseGC scope(masm); 951 __ Mov(saved_lr, lr); 952 __ Fmov(base_double, base_double_copy); 953 __ Scvtf(exponent_double, exponent_integer); 954 __ CallCFunction( 955 ExternalReference::power_double_double_function(isolate()), 956 0, 2); 957 __ Mov(lr, saved_lr); 958 __ Bind(&done); 959 __ IncrementCounter( 960 isolate()->counters()->math_pow(), 1, scratch0, scratch1); 961 __ Ret(); 962 } 963 } 964 965 966 void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { 967 // It is important that the following stubs are generated in this order 968 // because pregenerated stubs can only call other pregenerated stubs. 969 // RecordWriteStub uses StoreBufferOverflowStub, which in turn uses 970 // CEntryStub. 971 CEntryStub::GenerateAheadOfTime(isolate); 972 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); 973 StubFailureTrampolineStub::GenerateAheadOfTime(isolate); 974 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); 975 CreateAllocationSiteStub::GenerateAheadOfTime(isolate); 976 BinaryOpICStub::GenerateAheadOfTime(isolate); 977 StoreRegistersStateStub::GenerateAheadOfTime(isolate); 978 RestoreRegistersStateStub::GenerateAheadOfTime(isolate); 979 BinaryOpICWithAllocationSiteStub::GenerateAheadOfTime(isolate); 980 } 981 982 983 void StoreRegistersStateStub::GenerateAheadOfTime(Isolate* isolate) { 984 StoreRegistersStateStub stub(isolate); 985 stub.GetCode(); 986 } 987 988 989 void RestoreRegistersStateStub::GenerateAheadOfTime(Isolate* isolate) { 990 RestoreRegistersStateStub stub(isolate); 991 stub.GetCode(); 992 } 993 994 995 void CodeStub::GenerateFPStubs(Isolate* isolate) { 996 // Floating-point code doesn't get special handling in ARM64, so there's 997 // nothing to do here. 998 USE(isolate); 999 } 1000 1001 1002 bool CEntryStub::NeedsImmovableCode() { 1003 // CEntryStub stores the return address on the stack before calling into 1004 // C++ code. In some cases, the VM accesses this address, but it is not used 1005 // when the C++ code returns to the stub because LR holds the return address 1006 // in AAPCS64. If the stub is moved (perhaps during a GC), we could end up 1007 // returning to dead code. 1008 // TODO(jbramley): Whilst this is the only analysis that makes sense, I can't 1009 // find any comment to confirm this, and I don't hit any crashes whatever 1010 // this function returns. The anaylsis should be properly confirmed. 1011 return true; 1012 } 1013 1014 1015 void CEntryStub::GenerateAheadOfTime(Isolate* isolate) { 1016 CEntryStub stub(isolate, 1, kDontSaveFPRegs); 1017 stub.GetCode(); 1018 CEntryStub stub_fp(isolate, 1, kSaveFPRegs); 1019 stub_fp.GetCode(); 1020 } 1021 1022 1023 void CEntryStub::Generate(MacroAssembler* masm) { 1024 // The Abort mechanism relies on CallRuntime, which in turn relies on 1025 // CEntryStub, so until this stub has been generated, we have to use a 1026 // fall-back Abort mechanism. 1027 // 1028 // Note that this stub must be generated before any use of Abort. 1029 MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm); 1030 1031 ASM_LOCATION("CEntryStub::Generate entry"); 1032 ProfileEntryHookStub::MaybeCallEntryHook(masm); 1033 1034 // Register parameters: 1035 // x0: argc (including receiver, untagged) 1036 // x1: target 1037 // 1038 // The stack on entry holds the arguments and the receiver, with the receiver 1039 // at the highest address: 1040 // 1041 // jssp]argc-1]: receiver 1042 // jssp[argc-2]: arg[argc-2] 1043 // ... ... 1044 // jssp[1]: arg[1] 1045 // jssp[0]: arg[0] 1046 // 1047 // The arguments are in reverse order, so that arg[argc-2] is actually the 1048 // first argument to the target function and arg[0] is the last. 1049 DCHECK(jssp.Is(__ StackPointer())); 1050 const Register& argc_input = x0; 1051 const Register& target_input = x1; 1052 1053 // Calculate argv, argc and the target address, and store them in 1054 // callee-saved registers so we can retry the call without having to reload 1055 // these arguments. 1056 // TODO(jbramley): If the first call attempt succeeds in the common case (as 1057 // it should), then we might be better off putting these parameters directly 1058 // into their argument registers, rather than using callee-saved registers and 1059 // preserving them on the stack. 1060 const Register& argv = x21; 1061 const Register& argc = x22; 1062 const Register& target = x23; 1063 1064 // Derive argv from the stack pointer so that it points to the first argument 1065 // (arg[argc-2]), or just below the receiver in case there are no arguments. 1066 // - Adjust for the arg[] array. 1067 Register temp_argv = x11; 1068 __ Add(temp_argv, jssp, Operand(x0, LSL, kPointerSizeLog2)); 1069 // - Adjust for the receiver. 1070 __ Sub(temp_argv, temp_argv, 1 * kPointerSize); 1071 1072 // Enter the exit frame. Reserve three slots to preserve x21-x23 callee-saved 1073 // registers. 1074 FrameScope scope(masm, StackFrame::MANUAL); 1075 __ EnterExitFrame(save_doubles(), x10, 3); 1076 DCHECK(csp.Is(__ StackPointer())); 1077 1078 // Poke callee-saved registers into reserved space. 1079 __ Poke(argv, 1 * kPointerSize); 1080 __ Poke(argc, 2 * kPointerSize); 1081 __ Poke(target, 3 * kPointerSize); 1082 1083 // We normally only keep tagged values in callee-saved registers, as they 1084 // could be pushed onto the stack by called stubs and functions, and on the 1085 // stack they can confuse the GC. However, we're only calling C functions 1086 // which can push arbitrary data onto the stack anyway, and so the GC won't 1087 // examine that part of the stack. 1088 __ Mov(argc, argc_input); 1089 __ Mov(target, target_input); 1090 __ Mov(argv, temp_argv); 1091 1092 // x21 : argv 1093 // x22 : argc 1094 // x23 : call target 1095 // 1096 // The stack (on entry) holds the arguments and the receiver, with the 1097 // receiver at the highest address: 1098 // 1099 // argv[8]: receiver 1100 // argv -> argv[0]: arg[argc-2] 1101 // ... ... 1102 // argv[...]: arg[1] 1103 // argv[...]: arg[0] 1104 // 1105 // Immediately below (after) this is the exit frame, as constructed by 1106 // EnterExitFrame: 1107 // fp[8]: CallerPC (lr) 1108 // fp -> fp[0]: CallerFP (old fp) 1109 // fp[-8]: Space reserved for SPOffset. 1110 // fp[-16]: CodeObject() 1111 // csp[...]: Saved doubles, if saved_doubles is true. 1112 // csp[32]: Alignment padding, if necessary. 1113 // csp[24]: Preserved x23 (used for target). 1114 // csp[16]: Preserved x22 (used for argc). 1115 // csp[8]: Preserved x21 (used for argv). 1116 // csp -> csp[0]: Space reserved for the return address. 1117 // 1118 // After a successful call, the exit frame, preserved registers (x21-x23) and 1119 // the arguments (including the receiver) are dropped or popped as 1120 // appropriate. The stub then returns. 1121 // 1122 // After an unsuccessful call, the exit frame and suchlike are left 1123 // untouched, and the stub either throws an exception by jumping to one of 1124 // the exception_returned label. 1125 1126 DCHECK(csp.Is(__ StackPointer())); 1127 1128 // Prepare AAPCS64 arguments to pass to the builtin. 1129 __ Mov(x0, argc); 1130 __ Mov(x1, argv); 1131 __ Mov(x2, ExternalReference::isolate_address(isolate())); 1132 1133 Label return_location; 1134 __ Adr(x12, &return_location); 1135 __ Poke(x12, 0); 1136 1137 if (__ emit_debug_code()) { 1138 // Verify that the slot below fp[kSPOffset]-8 points to the return location 1139 // (currently in x12). 1140 UseScratchRegisterScope temps(masm); 1141 Register temp = temps.AcquireX(); 1142 __ Ldr(temp, MemOperand(fp, ExitFrameConstants::kSPOffset)); 1143 __ Ldr(temp, MemOperand(temp, -static_cast<int64_t>(kXRegSize))); 1144 __ Cmp(temp, x12); 1145 __ Check(eq, kReturnAddressNotFoundInFrame); 1146 } 1147 1148 // Call the builtin. 1149 __ Blr(target); 1150 __ Bind(&return_location); 1151 1152 // x0 result The return code from the call. 1153 // x21 argv 1154 // x22 argc 1155 // x23 target 1156 const Register& result = x0; 1157 1158 // Check result for exception sentinel. 1159 Label exception_returned; 1160 __ CompareRoot(result, Heap::kExceptionRootIndex); 1161 __ B(eq, &exception_returned); 1162 1163 // The call succeeded, so unwind the stack and return. 1164 1165 // Restore callee-saved registers x21-x23. 1166 __ Mov(x11, argc); 1167 1168 __ Peek(argv, 1 * kPointerSize); 1169 __ Peek(argc, 2 * kPointerSize); 1170 __ Peek(target, 3 * kPointerSize); 1171 1172 __ LeaveExitFrame(save_doubles(), x10, true); 1173 DCHECK(jssp.Is(__ StackPointer())); 1174 // Pop or drop the remaining stack slots and return from the stub. 1175 // jssp[24]: Arguments array (of size argc), including receiver. 1176 // jssp[16]: Preserved x23 (used for target). 1177 // jssp[8]: Preserved x22 (used for argc). 1178 // jssp[0]: Preserved x21 (used for argv). 1179 __ Drop(x11); 1180 __ AssertFPCRState(); 1181 __ Ret(); 1182 1183 // The stack pointer is still csp if we aren't returning, and the frame 1184 // hasn't changed (except for the return address). 1185 __ SetStackPointer(csp); 1186 1187 // Handling of exception. 1188 __ Bind(&exception_returned); 1189 1190 // Retrieve the pending exception. 1191 ExternalReference pending_exception_address( 1192 Isolate::kPendingExceptionAddress, isolate()); 1193 const Register& exception = result; 1194 const Register& exception_address = x11; 1195 __ Mov(exception_address, Operand(pending_exception_address)); 1196 __ Ldr(exception, MemOperand(exception_address)); 1197 1198 // Clear the pending exception. 1199 __ Mov(x10, Operand(isolate()->factory()->the_hole_value())); 1200 __ Str(x10, MemOperand(exception_address)); 1201 1202 // x0 exception The exception descriptor. 1203 // x21 argv 1204 // x22 argc 1205 // x23 target 1206 1207 // Special handling of termination exceptions, which are uncatchable by 1208 // JavaScript code. 1209 Label throw_termination_exception; 1210 __ Cmp(exception, Operand(isolate()->factory()->termination_exception())); 1211 __ B(eq, &throw_termination_exception); 1212 1213 // We didn't execute a return case, so the stack frame hasn't been updated 1214 // (except for the return address slot). However, we don't need to initialize 1215 // jssp because the throw method will immediately overwrite it when it 1216 // unwinds the stack. 1217 __ SetStackPointer(jssp); 1218 1219 ASM_LOCATION("Throw normal"); 1220 __ Mov(argv, 0); 1221 __ Mov(argc, 0); 1222 __ Mov(target, 0); 1223 __ Throw(x0, x10, x11, x12, x13); 1224 1225 __ Bind(&throw_termination_exception); 1226 ASM_LOCATION("Throw termination"); 1227 __ Mov(argv, 0); 1228 __ Mov(argc, 0); 1229 __ Mov(target, 0); 1230 __ ThrowUncatchable(x0, x10, x11, x12, x13); 1231 } 1232 1233 1234 // This is the entry point from C++. 5 arguments are provided in x0-x4. 1235 // See use of the CALL_GENERATED_CODE macro for example in src/execution.cc. 1236 // Input: 1237 // x0: code entry. 1238 // x1: function. 1239 // x2: receiver. 1240 // x3: argc. 1241 // x4: argv. 1242 // Output: 1243 // x0: result. 1244 void JSEntryStub::Generate(MacroAssembler* masm) { 1245 DCHECK(jssp.Is(__ StackPointer())); 1246 Register code_entry = x0; 1247 1248 // Enable instruction instrumentation. This only works on the simulator, and 1249 // will have no effect on the model or real hardware. 1250 __ EnableInstrumentation(); 1251 1252 Label invoke, handler_entry, exit; 1253 1254 // Push callee-saved registers and synchronize the system stack pointer (csp) 1255 // and the JavaScript stack pointer (jssp). 1256 // 1257 // We must not write to jssp until after the PushCalleeSavedRegisters() 1258 // call, since jssp is itself a callee-saved register. 1259 __ SetStackPointer(csp); 1260 __ PushCalleeSavedRegisters(); 1261 __ Mov(jssp, csp); 1262 __ SetStackPointer(jssp); 1263 1264 // Configure the FPCR. We don't restore it, so this is technically not allowed 1265 // according to AAPCS64. However, we only set default-NaN mode and this will 1266 // be harmless for most C code. Also, it works for ARM. 1267 __ ConfigureFPCR(); 1268 1269 ProfileEntryHookStub::MaybeCallEntryHook(masm); 1270 1271 // Set up the reserved register for 0.0. 1272 __ Fmov(fp_zero, 0.0); 1273 1274 // Build an entry frame (see layout below). 1275 int marker = type(); 1276 int64_t bad_frame_pointer = -1L; // Bad frame pointer to fail if it is used. 1277 __ Mov(x13, bad_frame_pointer); 1278 __ Mov(x12, Smi::FromInt(marker)); 1279 __ Mov(x11, ExternalReference(Isolate::kCEntryFPAddress, isolate())); 1280 __ Ldr(x10, MemOperand(x11)); 1281 1282 __ Push(x13, xzr, x12, x10); 1283 // Set up fp. 1284 __ Sub(fp, jssp, EntryFrameConstants::kCallerFPOffset); 1285 1286 // Push the JS entry frame marker. Also set js_entry_sp if this is the 1287 // outermost JS call. 1288 Label non_outermost_js, done; 1289 ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, isolate()); 1290 __ Mov(x10, ExternalReference(js_entry_sp)); 1291 __ Ldr(x11, MemOperand(x10)); 1292 __ Cbnz(x11, &non_outermost_js); 1293 __ Str(fp, MemOperand(x10)); 1294 __ Mov(x12, Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)); 1295 __ Push(x12); 1296 __ B(&done); 1297 __ Bind(&non_outermost_js); 1298 // We spare one instruction by pushing xzr since the marker is 0. 1299 DCHECK(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME) == NULL); 1300 __ Push(xzr); 1301 __ Bind(&done); 1302 1303 // The frame set up looks like this: 1304 // jssp[0] : JS entry frame marker. 1305 // jssp[1] : C entry FP. 1306 // jssp[2] : stack frame marker. 1307 // jssp[3] : stack frmae marker. 1308 // jssp[4] : bad frame pointer 0xfff...ff <- fp points here. 1309 1310 1311 // Jump to a faked try block that does the invoke, with a faked catch 1312 // block that sets the pending exception. 1313 __ B(&invoke); 1314 1315 // Prevent the constant pool from being emitted between the record of the 1316 // handler_entry position and the first instruction of the sequence here. 1317 // There is no risk because Assembler::Emit() emits the instruction before 1318 // checking for constant pool emission, but we do not want to depend on 1319 // that. 1320 { 1321 Assembler::BlockPoolsScope block_pools(masm); 1322 __ bind(&handler_entry); 1323 handler_offset_ = handler_entry.pos(); 1324 // Caught exception: Store result (exception) in the pending exception 1325 // field in the JSEnv and return a failure sentinel. Coming in here the 1326 // fp will be invalid because the PushTryHandler below sets it to 0 to 1327 // signal the existence of the JSEntry frame. 1328 __ Mov(x10, Operand(ExternalReference(Isolate::kPendingExceptionAddress, 1329 isolate()))); 1330 } 1331 __ Str(code_entry, MemOperand(x10)); 1332 __ LoadRoot(x0, Heap::kExceptionRootIndex); 1333 __ B(&exit); 1334 1335 // Invoke: Link this frame into the handler chain. There's only one 1336 // handler block in this code object, so its index is 0. 1337 __ Bind(&invoke); 1338 __ PushTryHandler(StackHandler::JS_ENTRY, 0); 1339 // If an exception not caught by another handler occurs, this handler 1340 // returns control to the code after the B(&invoke) above, which 1341 // restores all callee-saved registers (including cp and fp) to their 1342 // saved values before returning a failure to C. 1343 1344 // Clear any pending exceptions. 1345 __ Mov(x10, Operand(isolate()->factory()->the_hole_value())); 1346 __ Mov(x11, Operand(ExternalReference(Isolate::kPendingExceptionAddress, 1347 isolate()))); 1348 __ Str(x10, MemOperand(x11)); 1349 1350 // Invoke the function by calling through the JS entry trampoline builtin. 1351 // Notice that we cannot store a reference to the trampoline code directly in 1352 // this stub, because runtime stubs are not traversed when doing GC. 1353 1354 // Expected registers by Builtins::JSEntryTrampoline 1355 // x0: code entry. 1356 // x1: function. 1357 // x2: receiver. 1358 // x3: argc. 1359 // x4: argv. 1360 ExternalReference entry(type() == StackFrame::ENTRY_CONSTRUCT 1361 ? Builtins::kJSConstructEntryTrampoline 1362 : Builtins::kJSEntryTrampoline, 1363 isolate()); 1364 __ Mov(x10, entry); 1365 1366 // Call the JSEntryTrampoline. 1367 __ Ldr(x11, MemOperand(x10)); // Dereference the address. 1368 __ Add(x12, x11, Code::kHeaderSize - kHeapObjectTag); 1369 __ Blr(x12); 1370 1371 // Unlink this frame from the handler chain. 1372 __ PopTryHandler(); 1373 1374 1375 __ Bind(&exit); 1376 // x0 holds the result. 1377 // The stack pointer points to the top of the entry frame pushed on entry from 1378 // C++ (at the beginning of this stub): 1379 // jssp[0] : JS entry frame marker. 1380 // jssp[1] : C entry FP. 1381 // jssp[2] : stack frame marker. 1382 // jssp[3] : stack frmae marker. 1383 // jssp[4] : bad frame pointer 0xfff...ff <- fp points here. 1384 1385 // Check if the current stack frame is marked as the outermost JS frame. 1386 Label non_outermost_js_2; 1387 __ Pop(x10); 1388 __ Cmp(x10, Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)); 1389 __ B(ne, &non_outermost_js_2); 1390 __ Mov(x11, ExternalReference(js_entry_sp)); 1391 __ Str(xzr, MemOperand(x11)); 1392 __ Bind(&non_outermost_js_2); 1393 1394 // Restore the top frame descriptors from the stack. 1395 __ Pop(x10); 1396 __ Mov(x11, ExternalReference(Isolate::kCEntryFPAddress, isolate())); 1397 __ Str(x10, MemOperand(x11)); 1398 1399 // Reset the stack to the callee saved registers. 1400 __ Drop(-EntryFrameConstants::kCallerFPOffset, kByteSizeInBytes); 1401 // Restore the callee-saved registers and return. 1402 DCHECK(jssp.Is(__ StackPointer())); 1403 __ Mov(csp, jssp); 1404 __ SetStackPointer(csp); 1405 __ PopCalleeSavedRegisters(); 1406 // After this point, we must not modify jssp because it is a callee-saved 1407 // register which we have just restored. 1408 __ Ret(); 1409 } 1410 1411 1412 void FunctionPrototypeStub::Generate(MacroAssembler* masm) { 1413 Label miss; 1414 Register receiver = LoadDescriptor::ReceiverRegister(); 1415 1416 NamedLoadHandlerCompiler::GenerateLoadFunctionPrototype(masm, receiver, x10, 1417 x11, &miss); 1418 1419 __ Bind(&miss); 1420 PropertyAccessCompiler::TailCallBuiltin( 1421 masm, PropertyAccessCompiler::MissBuiltin(Code::LOAD_IC)); 1422 } 1423 1424 1425 void InstanceofStub::Generate(MacroAssembler* masm) { 1426 // Stack on entry: 1427 // jssp[0]: function. 1428 // jssp[8]: object. 1429 // 1430 // Returns result in x0. Zero indicates instanceof, smi 1 indicates not 1431 // instanceof. 1432 1433 Register result = x0; 1434 Register function = right(); 1435 Register object = left(); 1436 Register scratch1 = x6; 1437 Register scratch2 = x7; 1438 Register res_true = x8; 1439 Register res_false = x9; 1440 // Only used if there was an inline map check site. (See 1441 // LCodeGen::DoInstanceOfKnownGlobal().) 1442 Register map_check_site = x4; 1443 // Delta for the instructions generated between the inline map check and the 1444 // instruction setting the result. 1445 const int32_t kDeltaToLoadBoolResult = 4 * kInstructionSize; 1446 1447 Label not_js_object, slow; 1448 1449 if (!HasArgsInRegisters()) { 1450 __ Pop(function, object); 1451 } 1452 1453 if (ReturnTrueFalseObject()) { 1454 __ LoadTrueFalseRoots(res_true, res_false); 1455 } else { 1456 // This is counter-intuitive, but correct. 1457 __ Mov(res_true, Smi::FromInt(0)); 1458 __ Mov(res_false, Smi::FromInt(1)); 1459 } 1460 1461 // Check that the left hand side is a JS object and load its map as a side 1462 // effect. 1463 Register map = x12; 1464 __ JumpIfSmi(object, ¬_js_object); 1465 __ IsObjectJSObjectType(object, map, scratch2, ¬_js_object); 1466 1467 // If there is a call site cache, don't look in the global cache, but do the 1468 // real lookup and update the call site cache. 1469 if (!HasCallSiteInlineCheck() && !ReturnTrueFalseObject()) { 1470 Label miss; 1471 __ JumpIfNotRoot(function, Heap::kInstanceofCacheFunctionRootIndex, &miss); 1472 __ JumpIfNotRoot(map, Heap::kInstanceofCacheMapRootIndex, &miss); 1473 __ LoadRoot(result, Heap::kInstanceofCacheAnswerRootIndex); 1474 __ Ret(); 1475 __ Bind(&miss); 1476 } 1477 1478 // Get the prototype of the function. 1479 Register prototype = x13; 1480 __ TryGetFunctionPrototype(function, prototype, scratch2, &slow, 1481 MacroAssembler::kMissOnBoundFunction); 1482 1483 // Check that the function prototype is a JS object. 1484 __ JumpIfSmi(prototype, &slow); 1485 __ IsObjectJSObjectType(prototype, scratch1, scratch2, &slow); 1486 1487 // Update the global instanceof or call site inlined cache with the current 1488 // map and function. The cached answer will be set when it is known below. 1489 if (HasCallSiteInlineCheck()) { 1490 // Patch the (relocated) inlined map check. 1491 __ GetRelocatedValueLocation(map_check_site, scratch1); 1492 // We have a cell, so need another level of dereferencing. 1493 __ Ldr(scratch1, MemOperand(scratch1)); 1494 __ Str(map, FieldMemOperand(scratch1, Cell::kValueOffset)); 1495 } else { 1496 __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex); 1497 __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex); 1498 } 1499 1500 Label return_true, return_result; 1501 Register smi_value = scratch1; 1502 { 1503 // Loop through the prototype chain looking for the function prototype. 1504 Register chain_map = x1; 1505 Register chain_prototype = x14; 1506 Register null_value = x15; 1507 Label loop; 1508 __ Ldr(chain_prototype, FieldMemOperand(map, Map::kPrototypeOffset)); 1509 __ LoadRoot(null_value, Heap::kNullValueRootIndex); 1510 // Speculatively set a result. 1511 __ Mov(result, res_false); 1512 if (!HasCallSiteInlineCheck() && ReturnTrueFalseObject()) { 1513 // Value to store in the cache cannot be an object. 1514 __ Mov(smi_value, Smi::FromInt(1)); 1515 } 1516 1517 __ Bind(&loop); 1518 1519 // If the chain prototype is the object prototype, return true. 1520 __ Cmp(chain_prototype, prototype); 1521 __ B(eq, &return_true); 1522 1523 // If the chain prototype is null, we've reached the end of the chain, so 1524 // return false. 1525 __ Cmp(chain_prototype, null_value); 1526 __ B(eq, &return_result); 1527 1528 // Otherwise, load the next prototype in the chain, and loop. 1529 __ Ldr(chain_map, FieldMemOperand(chain_prototype, HeapObject::kMapOffset)); 1530 __ Ldr(chain_prototype, FieldMemOperand(chain_map, Map::kPrototypeOffset)); 1531 __ B(&loop); 1532 } 1533 1534 // Return sequence when no arguments are on the stack. 1535 // We cannot fall through to here. 1536 __ Bind(&return_true); 1537 __ Mov(result, res_true); 1538 if (!HasCallSiteInlineCheck() && ReturnTrueFalseObject()) { 1539 // Value to store in the cache cannot be an object. 1540 __ Mov(smi_value, Smi::FromInt(0)); 1541 } 1542 __ Bind(&return_result); 1543 if (HasCallSiteInlineCheck()) { 1544 DCHECK(ReturnTrueFalseObject()); 1545 __ Add(map_check_site, map_check_site, kDeltaToLoadBoolResult); 1546 __ GetRelocatedValueLocation(map_check_site, scratch2); 1547 __ Str(result, MemOperand(scratch2)); 1548 } else { 1549 Register cached_value = ReturnTrueFalseObject() ? smi_value : result; 1550 __ StoreRoot(cached_value, Heap::kInstanceofCacheAnswerRootIndex); 1551 } 1552 __ Ret(); 1553 1554 Label object_not_null, object_not_null_or_smi; 1555 1556 __ Bind(¬_js_object); 1557 Register object_type = x14; 1558 // x0 result result return register (uninit) 1559 // x10 function pointer to function 1560 // x11 object pointer to object 1561 // x14 object_type type of object (uninit) 1562 1563 // Before null, smi and string checks, check that the rhs is a function. 1564 // For a non-function rhs, an exception must be thrown. 1565 __ JumpIfSmi(function, &slow); 1566 __ JumpIfNotObjectType( 1567 function, scratch1, object_type, JS_FUNCTION_TYPE, &slow); 1568 1569 __ Mov(result, res_false); 1570 1571 // Null is not instance of anything. 1572 __ Cmp(object_type, Operand(isolate()->factory()->null_value())); 1573 __ B(ne, &object_not_null); 1574 __ Ret(); 1575 1576 __ Bind(&object_not_null); 1577 // Smi values are not instances of anything. 1578 __ JumpIfNotSmi(object, &object_not_null_or_smi); 1579 __ Ret(); 1580 1581 __ Bind(&object_not_null_or_smi); 1582 // String values are not instances of anything. 1583 __ IsObjectJSStringType(object, scratch2, &slow); 1584 __ Ret(); 1585 1586 // Slow-case. Tail call builtin. 1587 __ Bind(&slow); 1588 { 1589 FrameScope scope(masm, StackFrame::INTERNAL); 1590 // Arguments have either been passed into registers or have been previously 1591 // popped. We need to push them before calling builtin. 1592 __ Push(object, function); 1593 __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); 1594 } 1595 if (ReturnTrueFalseObject()) { 1596 // Reload true/false because they were clobbered in the builtin call. 1597 __ LoadTrueFalseRoots(res_true, res_false); 1598 __ Cmp(result, 0); 1599 __ Csel(result, res_true, res_false, eq); 1600 } 1601 __ Ret(); 1602 } 1603 1604 1605 void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { 1606 Register arg_count = ArgumentsAccessReadDescriptor::parameter_count(); 1607 Register key = ArgumentsAccessReadDescriptor::index(); 1608 DCHECK(arg_count.is(x0)); 1609 DCHECK(key.is(x1)); 1610 1611 // The displacement is the offset of the last parameter (if any) relative 1612 // to the frame pointer. 1613 static const int kDisplacement = 1614 StandardFrameConstants::kCallerSPOffset - kPointerSize; 1615 1616 // Check that the key is a smi. 1617 Label slow; 1618 __ JumpIfNotSmi(key, &slow); 1619 1620 // Check if the calling frame is an arguments adaptor frame. 1621 Register local_fp = x11; 1622 Register caller_fp = x11; 1623 Register caller_ctx = x12; 1624 Label skip_adaptor; 1625 __ Ldr(caller_fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); 1626 __ Ldr(caller_ctx, MemOperand(caller_fp, 1627 StandardFrameConstants::kContextOffset)); 1628 __ Cmp(caller_ctx, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); 1629 __ Csel(local_fp, fp, caller_fp, ne); 1630 __ B(ne, &skip_adaptor); 1631 1632 // Load the actual arguments limit found in the arguments adaptor frame. 1633 __ Ldr(arg_count, MemOperand(caller_fp, 1634 ArgumentsAdaptorFrameConstants::kLengthOffset)); 1635 __ Bind(&skip_adaptor); 1636 1637 // Check index against formal parameters count limit. Use unsigned comparison 1638 // to get negative check for free: branch if key < 0 or key >= arg_count. 1639 __ Cmp(key, arg_count); 1640 __ B(hs, &slow); 1641 1642 // Read the argument from the stack and return it. 1643 __ Sub(x10, arg_count, key); 1644 __ Add(x10, local_fp, Operand::UntagSmiAndScale(x10, kPointerSizeLog2)); 1645 __ Ldr(x0, MemOperand(x10, kDisplacement)); 1646 __ Ret(); 1647 1648 // Slow case: handle non-smi or out-of-bounds access to arguments by calling 1649 // the runtime system. 1650 __ Bind(&slow); 1651 __ Push(key); 1652 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1); 1653 } 1654 1655 1656 void ArgumentsAccessStub::GenerateNewSloppySlow(MacroAssembler* masm) { 1657 // Stack layout on entry. 1658 // jssp[0]: number of parameters (tagged) 1659 // jssp[8]: address of receiver argument 1660 // jssp[16]: function 1661 1662 // Check if the calling frame is an arguments adaptor frame. 1663 Label runtime; 1664 Register caller_fp = x10; 1665 __ Ldr(caller_fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); 1666 // Load and untag the context. 1667 __ Ldr(w11, UntagSmiMemOperand(caller_fp, 1668 StandardFrameConstants::kContextOffset)); 1669 __ Cmp(w11, StackFrame::ARGUMENTS_ADAPTOR); 1670 __ B(ne, &runtime); 1671 1672 // Patch the arguments.length and parameters pointer in the current frame. 1673 __ Ldr(x11, MemOperand(caller_fp, 1674 ArgumentsAdaptorFrameConstants::kLengthOffset)); 1675 __ Poke(x11, 0 * kXRegSize); 1676 __ Add(x10, caller_fp, Operand::UntagSmiAndScale(x11, kPointerSizeLog2)); 1677 __ Add(x10, x10, StandardFrameConstants::kCallerSPOffset); 1678 __ Poke(x10, 1 * kXRegSize); 1679 1680 __ Bind(&runtime); 1681 __ TailCallRuntime(Runtime::kNewSloppyArguments, 3, 1); 1682 } 1683 1684 1685 void ArgumentsAccessStub::GenerateNewSloppyFast(MacroAssembler* masm) { 1686 // Stack layout on entry. 1687 // jssp[0]: number of parameters (tagged) 1688 // jssp[8]: address of receiver argument 1689 // jssp[16]: function 1690 // 1691 // Returns pointer to result object in x0. 1692 1693 // Note: arg_count_smi is an alias of param_count_smi. 1694 Register arg_count_smi = x3; 1695 Register param_count_smi = x3; 1696 Register param_count = x7; 1697 Register recv_arg = x14; 1698 Register function = x4; 1699 __ Pop(param_count_smi, recv_arg, function); 1700 __ SmiUntag(param_count, param_count_smi); 1701 1702 // Check if the calling frame is an arguments adaptor frame. 1703 Register caller_fp = x11; 1704 Register caller_ctx = x12; 1705 Label runtime; 1706 Label adaptor_frame, try_allocate; 1707 __ Ldr(caller_fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); 1708 __ Ldr(caller_ctx, MemOperand(caller_fp, 1709 StandardFrameConstants::kContextOffset)); 1710 __ Cmp(caller_ctx, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); 1711 __ B(eq, &adaptor_frame); 1712 1713 // No adaptor, parameter count = argument count. 1714 1715 // x1 mapped_params number of mapped params, min(params, args) (uninit) 1716 // x2 arg_count number of function arguments (uninit) 1717 // x3 arg_count_smi number of function arguments (smi) 1718 // x4 function function pointer 1719 // x7 param_count number of function parameters 1720 // x11 caller_fp caller's frame pointer 1721 // x14 recv_arg pointer to receiver arguments 1722 1723 Register arg_count = x2; 1724 __ Mov(arg_count, param_count); 1725 __ B(&try_allocate); 1726 1727 // We have an adaptor frame. Patch the parameters pointer. 1728 __ Bind(&adaptor_frame); 1729 __ Ldr(arg_count_smi, 1730 MemOperand(caller_fp, 1731 ArgumentsAdaptorFrameConstants::kLengthOffset)); 1732 __ SmiUntag(arg_count, arg_count_smi); 1733 __ Add(x10, caller_fp, Operand(arg_count, LSL, kPointerSizeLog2)); 1734 __ Add(recv_arg, x10, StandardFrameConstants::kCallerSPOffset); 1735 1736 // Compute the mapped parameter count = min(param_count, arg_count) 1737 Register mapped_params = x1; 1738 __ Cmp(param_count, arg_count); 1739 __ Csel(mapped_params, param_count, arg_count, lt); 1740 1741 __ Bind(&try_allocate); 1742 1743 // x0 alloc_obj pointer to allocated objects: param map, backing 1744 // store, arguments (uninit) 1745 // x1 mapped_params number of mapped parameters, min(params, args) 1746 // x2 arg_count number of function arguments 1747 // x3 arg_count_smi number of function arguments (smi) 1748 // x4 function function pointer 1749 // x7 param_count number of function parameters 1750 // x10 size size of objects to allocate (uninit) 1751 // x14 recv_arg pointer to receiver arguments 1752 1753 // Compute the size of backing store, parameter map, and arguments object. 1754 // 1. Parameter map, has two extra words containing context and backing 1755 // store. 1756 const int kParameterMapHeaderSize = 1757 FixedArray::kHeaderSize + 2 * kPointerSize; 1758 1759 // Calculate the parameter map size, assuming it exists. 1760 Register size = x10; 1761 __ Mov(size, Operand(mapped_params, LSL, kPointerSizeLog2)); 1762 __ Add(size, size, kParameterMapHeaderSize); 1763 1764 // If there are no mapped parameters, set the running size total to zero. 1765 // Otherwise, use the parameter map size calculated earlier. 1766 __ Cmp(mapped_params, 0); 1767 __ CzeroX(size, eq); 1768 1769 // 2. Add the size of the backing store and arguments object. 1770 __ Add(size, size, Operand(arg_count, LSL, kPointerSizeLog2)); 1771 __ Add(size, size, 1772 FixedArray::kHeaderSize + Heap::kSloppyArgumentsObjectSize); 1773 1774 // Do the allocation of all three objects in one go. Assign this to x0, as it 1775 // will be returned to the caller. 1776 Register alloc_obj = x0; 1777 __ Allocate(size, alloc_obj, x11, x12, &runtime, TAG_OBJECT); 1778 1779 // Get the arguments boilerplate from the current (global) context. 1780 1781 // x0 alloc_obj pointer to allocated objects (param map, backing 1782 // store, arguments) 1783 // x1 mapped_params number of mapped parameters, min(params, args) 1784 // x2 arg_count number of function arguments 1785 // x3 arg_count_smi number of function arguments (smi) 1786 // x4 function function pointer 1787 // x7 param_count number of function parameters 1788 // x11 sloppy_args_map offset to args (or aliased args) map (uninit) 1789 // x14 recv_arg pointer to receiver arguments 1790 1791 Register global_object = x10; 1792 Register global_ctx = x10; 1793 Register sloppy_args_map = x11; 1794 Register aliased_args_map = x10; 1795 __ Ldr(global_object, GlobalObjectMemOperand()); 1796 __ Ldr(global_ctx, FieldMemOperand(global_object, 1797 GlobalObject::kNativeContextOffset)); 1798 1799 __ Ldr(sloppy_args_map, 1800 ContextMemOperand(global_ctx, Context::SLOPPY_ARGUMENTS_MAP_INDEX)); 1801 __ Ldr(aliased_args_map, 1802 ContextMemOperand(global_ctx, Context::ALIASED_ARGUMENTS_MAP_INDEX)); 1803 __ Cmp(mapped_params, 0); 1804 __ CmovX(sloppy_args_map, aliased_args_map, ne); 1805 1806 // Copy the JS object part. 1807 __ Str(sloppy_args_map, FieldMemOperand(alloc_obj, JSObject::kMapOffset)); 1808 __ LoadRoot(x10, Heap::kEmptyFixedArrayRootIndex); 1809 __ Str(x10, FieldMemOperand(alloc_obj, JSObject::kPropertiesOffset)); 1810 __ Str(x10, FieldMemOperand(alloc_obj, JSObject::kElementsOffset)); 1811 1812 // Set up the callee in-object property. 1813 STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1); 1814 const int kCalleeOffset = JSObject::kHeaderSize + 1815 Heap::kArgumentsCalleeIndex * kPointerSize; 1816 __ AssertNotSmi(function); 1817 __ Str(function, FieldMemOperand(alloc_obj, kCalleeOffset)); 1818 1819 // Use the length and set that as an in-object property. 1820 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0); 1821 const int kLengthOffset = JSObject::kHeaderSize + 1822 Heap::kArgumentsLengthIndex * kPointerSize; 1823 __ Str(arg_count_smi, FieldMemOperand(alloc_obj, kLengthOffset)); 1824 1825 // Set up the elements pointer in the allocated arguments object. 1826 // If we allocated a parameter map, "elements" will point there, otherwise 1827 // it will point to the backing store. 1828 1829 // x0 alloc_obj pointer to allocated objects (param map, backing 1830 // store, arguments) 1831 // x1 mapped_params number of mapped parameters, min(params, args) 1832 // x2 arg_count number of function arguments 1833 // x3 arg_count_smi number of function arguments (smi) 1834 // x4 function function pointer 1835 // x5 elements pointer to parameter map or backing store (uninit) 1836 // x6 backing_store pointer to backing store (uninit) 1837 // x7 param_count number of function parameters 1838 // x14 recv_arg pointer to receiver arguments 1839 1840 Register elements = x5; 1841 __ Add(elements, alloc_obj, Heap::kSloppyArgumentsObjectSize); 1842 __ Str(elements, FieldMemOperand(alloc_obj, JSObject::kElementsOffset)); 1843 1844 // Initialize parameter map. If there are no mapped arguments, we're done. 1845 Label skip_parameter_map; 1846 __ Cmp(mapped_params, 0); 1847 // Set up backing store address, because it is needed later for filling in 1848 // the unmapped arguments. 1849 Register backing_store = x6; 1850 __ CmovX(backing_store, elements, eq); 1851 __ B(eq, &skip_parameter_map); 1852 1853 __ LoadRoot(x10, Heap::kSloppyArgumentsElementsMapRootIndex); 1854 __ Str(x10, FieldMemOperand(elements, FixedArray::kMapOffset)); 1855 __ Add(x10, mapped_params, 2); 1856 __ SmiTag(x10); 1857 __ Str(x10, FieldMemOperand(elements, FixedArray::kLengthOffset)); 1858 __ Str(cp, FieldMemOperand(elements, 1859 FixedArray::kHeaderSize + 0 * kPointerSize)); 1860 __ Add(x10, elements, Operand(mapped_params, LSL, kPointerSizeLog2)); 1861 __ Add(x10, x10, kParameterMapHeaderSize); 1862 __ Str(x10, FieldMemOperand(elements, 1863 FixedArray::kHeaderSize + 1 * kPointerSize)); 1864 1865 // Copy the parameter slots and the holes in the arguments. 1866 // We need to fill in mapped_parameter_count slots. Then index the context, 1867 // where parameters are stored in reverse order, at: 1868 // 1869 // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS + parameter_count - 1 1870 // 1871 // The mapped parameter thus needs to get indices: 1872 // 1873 // MIN_CONTEXT_SLOTS + parameter_count - 1 .. 1874 // MIN_CONTEXT_SLOTS + parameter_count - mapped_parameter_count 1875 // 1876 // We loop from right to left. 1877 1878 // x0 alloc_obj pointer to allocated objects (param map, backing 1879 // store, arguments) 1880 // x1 mapped_params number of mapped parameters, min(params, args) 1881 // x2 arg_count number of function arguments 1882 // x3 arg_count_smi number of function arguments (smi) 1883 // x4 function function pointer 1884 // x5 elements pointer to parameter map or backing store (uninit) 1885 // x6 backing_store pointer to backing store (uninit) 1886 // x7 param_count number of function parameters 1887 // x11 loop_count parameter loop counter (uninit) 1888 // x12 index parameter index (smi, uninit) 1889 // x13 the_hole hole value (uninit) 1890 // x14 recv_arg pointer to receiver arguments 1891 1892 Register loop_count = x11; 1893 Register index = x12; 1894 Register the_hole = x13; 1895 Label parameters_loop, parameters_test; 1896 __ Mov(loop_count, mapped_params); 1897 __ Add(index, param_count, static_cast<int>(Context::MIN_CONTEXT_SLOTS)); 1898 __ Sub(index, index, mapped_params); 1899 __ SmiTag(index); 1900 __ LoadRoot(the_hole, Heap::kTheHoleValueRootIndex); 1901 __ Add(backing_store, elements, Operand(loop_count, LSL, kPointerSizeLog2)); 1902 __ Add(backing_store, backing_store, kParameterMapHeaderSize); 1903 1904 __ B(¶meters_test); 1905 1906 __ Bind(¶meters_loop); 1907 __ Sub(loop_count, loop_count, 1); 1908 __ Mov(x10, Operand(loop_count, LSL, kPointerSizeLog2)); 1909 __ Add(x10, x10, kParameterMapHeaderSize - kHeapObjectTag); 1910 __ Str(index, MemOperand(elements, x10)); 1911 __ Sub(x10, x10, kParameterMapHeaderSize - FixedArray::kHeaderSize); 1912 __ Str(the_hole, MemOperand(backing_store, x10)); 1913 __ Add(index, index, Smi::FromInt(1)); 1914 __ Bind(¶meters_test); 1915 __ Cbnz(loop_count, ¶meters_loop); 1916 1917 __ Bind(&skip_parameter_map); 1918 // Copy arguments header and remaining slots (if there are any.) 1919 __ LoadRoot(x10, Heap::kFixedArrayMapRootIndex); 1920 __ Str(x10, FieldMemOperand(backing_store, FixedArray::kMapOffset)); 1921 __ Str(arg_count_smi, FieldMemOperand(backing_store, 1922 FixedArray::kLengthOffset)); 1923 1924 // x0 alloc_obj pointer to allocated objects (param map, backing 1925 // store, arguments) 1926 // x1 mapped_params number of mapped parameters, min(params, args) 1927 // x2 arg_count number of function arguments 1928 // x4 function function pointer 1929 // x3 arg_count_smi number of function arguments (smi) 1930 // x6 backing_store pointer to backing store (uninit) 1931 // x14 recv_arg pointer to receiver arguments 1932 1933 Label arguments_loop, arguments_test; 1934 __ Mov(x10, mapped_params); 1935 __ Sub(recv_arg, recv_arg, Operand(x10, LSL, kPointerSizeLog2)); 1936 __ B(&arguments_test); 1937 1938 __ Bind(&arguments_loop); 1939 __ Sub(recv_arg, recv_arg, kPointerSize); 1940 __ Ldr(x11, MemOperand(recv_arg)); 1941 __ Add(x12, backing_store, Operand(x10, LSL, kPointerSizeLog2)); 1942 __ Str(x11, FieldMemOperand(x12, FixedArray::kHeaderSize)); 1943 __ Add(x10, x10, 1); 1944 1945 __ Bind(&arguments_test); 1946 __ Cmp(x10, arg_count); 1947 __ B(lt, &arguments_loop); 1948 1949 __ Ret(); 1950 1951 // Do the runtime call to allocate the arguments object. 1952 __ Bind(&runtime); 1953 __ Push(function, recv_arg, arg_count_smi); 1954 __ TailCallRuntime(Runtime::kNewSloppyArguments, 3, 1); 1955 } 1956 1957 1958 void LoadIndexedInterceptorStub::Generate(MacroAssembler* masm) { 1959 // Return address is in lr. 1960 Label slow; 1961 1962 Register receiver = LoadDescriptor::ReceiverRegister(); 1963 Register key = LoadDescriptor::NameRegister(); 1964 1965 // Check that the key is an array index, that is Uint32. 1966 __ TestAndBranchIfAnySet(key, kSmiTagMask | kSmiSignMask, &slow); 1967 1968 // Everything is fine, call runtime. 1969 __ Push(receiver, key); 1970 __ TailCallExternalReference( 1971 ExternalReference(IC_Utility(IC::kLoadElementWithInterceptor), 1972 masm->isolate()), 1973 2, 1); 1974 1975 __ Bind(&slow); 1976 PropertyAccessCompiler::TailCallBuiltin( 1977 masm, PropertyAccessCompiler::MissBuiltin(Code::KEYED_LOAD_IC)); 1978 } 1979 1980 1981 void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { 1982 // Stack layout on entry. 1983 // jssp[0]: number of parameters (tagged) 1984 // jssp[8]: address of receiver argument 1985 // jssp[16]: function 1986 // 1987 // Returns pointer to result object in x0. 1988 1989 // Get the stub arguments from the frame, and make an untagged copy of the 1990 // parameter count. 1991 Register param_count_smi = x1; 1992 Register params = x2; 1993 Register function = x3; 1994 Register param_count = x13; 1995 __ Pop(param_count_smi, params, function); 1996 __ SmiUntag(param_count, param_count_smi); 1997 1998 // Test if arguments adaptor needed. 1999 Register caller_fp = x11; 2000 Register caller_ctx = x12; 2001 Label try_allocate, runtime; 2002 __ Ldr(caller_fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); 2003 __ Ldr(caller_ctx, MemOperand(caller_fp, 2004 StandardFrameConstants::kContextOffset)); 2005 __ Cmp(caller_ctx, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); 2006 __ B(ne, &try_allocate); 2007 2008 // x1 param_count_smi number of parameters passed to function (smi) 2009 // x2 params pointer to parameters 2010 // x3 function function pointer 2011 // x11 caller_fp caller's frame pointer 2012 // x13 param_count number of parameters passed to function 2013 2014 // Patch the argument length and parameters pointer. 2015 __ Ldr(param_count_smi, 2016 MemOperand(caller_fp, 2017 ArgumentsAdaptorFrameConstants::kLengthOffset)); 2018 __ SmiUntag(param_count, param_count_smi); 2019 __ Add(x10, caller_fp, Operand(param_count, LSL, kPointerSizeLog2)); 2020 __ Add(params, x10, StandardFrameConstants::kCallerSPOffset); 2021 2022 // Try the new space allocation. Start out with computing the size of the 2023 // arguments object and the elements array in words. 2024 Register size = x10; 2025 __ Bind(&try_allocate); 2026 __ Add(size, param_count, FixedArray::kHeaderSize / kPointerSize); 2027 __ Cmp(param_count, 0); 2028 __ CzeroX(size, eq); 2029 __ Add(size, size, Heap::kStrictArgumentsObjectSize / kPointerSize); 2030 2031 // Do the allocation of both objects in one go. Assign this to x0, as it will 2032 // be returned to the caller. 2033 Register alloc_obj = x0; 2034 __ Allocate(size, alloc_obj, x11, x12, &runtime, 2035 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS)); 2036 2037 // Get the arguments boilerplate from the current (native) context. 2038 Register global_object = x10; 2039 Register global_ctx = x10; 2040 Register strict_args_map = x4; 2041 __ Ldr(global_object, GlobalObjectMemOperand()); 2042 __ Ldr(global_ctx, FieldMemOperand(global_object, 2043 GlobalObject::kNativeContextOffset)); 2044 __ Ldr(strict_args_map, 2045 ContextMemOperand(global_ctx, Context::STRICT_ARGUMENTS_MAP_INDEX)); 2046 2047 // x0 alloc_obj pointer to allocated objects: parameter array and 2048 // arguments object 2049 // x1 param_count_smi number of parameters passed to function (smi) 2050 // x2 params pointer to parameters 2051 // x3 function function pointer 2052 // x4 strict_args_map offset to arguments map 2053 // x13 param_count number of parameters passed to function 2054 __ Str(strict_args_map, FieldMemOperand(alloc_obj, JSObject::kMapOffset)); 2055 __ LoadRoot(x5, Heap::kEmptyFixedArrayRootIndex); 2056 __ Str(x5, FieldMemOperand(alloc_obj, JSObject::kPropertiesOffset)); 2057 __ Str(x5, FieldMemOperand(alloc_obj, JSObject::kElementsOffset)); 2058 2059 // Set the smi-tagged length as an in-object property. 2060 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0); 2061 const int kLengthOffset = JSObject::kHeaderSize + 2062 Heap::kArgumentsLengthIndex * kPointerSize; 2063 __ Str(param_count_smi, FieldMemOperand(alloc_obj, kLengthOffset)); 2064 2065 // If there are no actual arguments, we're done. 2066 Label done; 2067 __ Cbz(param_count, &done); 2068 2069 // Set up the elements pointer in the allocated arguments object and 2070 // initialize the header in the elements fixed array. 2071 Register elements = x5; 2072 __ Add(elements, alloc_obj, Heap::kStrictArgumentsObjectSize); 2073 __ Str(elements, FieldMemOperand(alloc_obj, JSObject::kElementsOffset)); 2074 __ LoadRoot(x10, Heap::kFixedArrayMapRootIndex); 2075 __ Str(x10, FieldMemOperand(elements, FixedArray::kMapOffset)); 2076 __ Str(param_count_smi, FieldMemOperand(elements, FixedArray::kLengthOffset)); 2077 2078 // x0 alloc_obj pointer to allocated objects: parameter array and 2079 // arguments object 2080 // x1 param_count_smi number of parameters passed to function (smi) 2081 // x2 params pointer to parameters 2082 // x3 function function pointer 2083 // x4 array pointer to array slot (uninit) 2084 // x5 elements pointer to elements array of alloc_obj 2085 // x13 param_count number of parameters passed to function 2086 2087 // Copy the fixed array slots. 2088 Label loop; 2089 Register array = x4; 2090 // Set up pointer to first array slot. 2091 __ Add(array, elements, FixedArray::kHeaderSize - kHeapObjectTag); 2092 2093 __ Bind(&loop); 2094 // Pre-decrement the parameters pointer by kPointerSize on each iteration. 2095 // Pre-decrement in order to skip receiver. 2096 __ Ldr(x10, MemOperand(params, -kPointerSize, PreIndex)); 2097 // Post-increment elements by kPointerSize on each iteration. 2098 __ Str(x10, MemOperand(array, kPointerSize, PostIndex)); 2099 __ Sub(param_count, param_count, 1); 2100 __ Cbnz(param_count, &loop); 2101 2102 // Return from stub. 2103 __ Bind(&done); 2104 __ Ret(); 2105 2106 // Do the runtime call to allocate the arguments object. 2107 __ Bind(&runtime); 2108 __ Push(function, params, param_count_smi); 2109 __ TailCallRuntime(Runtime::kNewStrictArguments, 3, 1); 2110 } 2111 2112 2113 void RegExpExecStub::Generate(MacroAssembler* masm) { 2114 #ifdef V8_INTERPRETED_REGEXP 2115 __ TailCallRuntime(Runtime::kRegExpExecRT, 4, 1); 2116 #else // V8_INTERPRETED_REGEXP 2117 2118 // Stack frame on entry. 2119 // jssp[0]: last_match_info (expected JSArray) 2120 // jssp[8]: previous index 2121 // jssp[16]: subject string 2122 // jssp[24]: JSRegExp object 2123 Label runtime; 2124 2125 // Use of registers for this function. 2126 2127 // Variable registers: 2128 // x10-x13 used as scratch registers 2129 // w0 string_type type of subject string 2130 // x2 jsstring_length subject string length 2131 // x3 jsregexp_object JSRegExp object 2132 // w4 string_encoding Latin1 or UC16 2133 // w5 sliced_string_offset if the string is a SlicedString 2134 // offset to the underlying string 2135 // w6 string_representation groups attributes of the string: 2136 // - is a string 2137 // - type of the string 2138 // - is a short external string 2139 Register string_type = w0; 2140 Register jsstring_length = x2; 2141 Register jsregexp_object = x3; 2142 Register string_encoding = w4; 2143 Register sliced_string_offset = w5; 2144 Register string_representation = w6; 2145 2146 // These are in callee save registers and will be preserved by the call 2147 // to the native RegExp code, as this code is called using the normal 2148 // C calling convention. When calling directly from generated code the 2149 // native RegExp code will not do a GC and therefore the content of 2150 // these registers are safe to use after the call. 2151 2152 // x19 subject subject string 2153 // x20 regexp_data RegExp data (FixedArray) 2154 // x21 last_match_info_elements info relative to the last match 2155 // (FixedArray) 2156 // x22 code_object generated regexp code 2157 Register subject = x19; 2158 Register regexp_data = x20; 2159 Register last_match_info_elements = x21; 2160 Register code_object = x22; 2161 2162 // TODO(jbramley): Is it necessary to preserve these? I don't think ARM does. 2163 CPURegList used_callee_saved_registers(subject, 2164 regexp_data, 2165 last_match_info_elements, 2166 code_object); 2167 __ PushCPURegList(used_callee_saved_registers); 2168 2169 // Stack frame. 2170 // jssp[0] : x19 2171 // jssp[8] : x20 2172 // jssp[16]: x21 2173 // jssp[24]: x22 2174 // jssp[32]: last_match_info (JSArray) 2175 // jssp[40]: previous index 2176 // jssp[48]: subject string 2177 // jssp[56]: JSRegExp object 2178 2179 const int kLastMatchInfoOffset = 4 * kPointerSize; 2180 const int kPreviousIndexOffset = 5 * kPointerSize; 2181 const int kSubjectOffset = 6 * kPointerSize; 2182 const int kJSRegExpOffset = 7 * kPointerSize; 2183 2184 // Ensure that a RegExp stack is allocated. 2185 ExternalReference address_of_regexp_stack_memory_address = 2186 ExternalReference::address_of_regexp_stack_memory_address(isolate()); 2187 ExternalReference address_of_regexp_stack_memory_size = 2188 ExternalReference::address_of_regexp_stack_memory_size(isolate()); 2189 __ Mov(x10, address_of_regexp_stack_memory_size); 2190 __ Ldr(x10, MemOperand(x10)); 2191 __ Cbz(x10, &runtime); 2192 2193 // Check that the first argument is a JSRegExp object. 2194 DCHECK(jssp.Is(__ StackPointer())); 2195 __ Peek(jsregexp_object, kJSRegExpOffset); 2196 __ JumpIfSmi(jsregexp_object, &runtime); 2197 __ JumpIfNotObjectType(jsregexp_object, x10, x10, JS_REGEXP_TYPE, &runtime); 2198 2199 // Check that the RegExp has been compiled (data contains a fixed array). 2200 __ Ldr(regexp_data, FieldMemOperand(jsregexp_object, JSRegExp::kDataOffset)); 2201 if (FLAG_debug_code) { 2202 STATIC_ASSERT(kSmiTag == 0); 2203 __ Tst(regexp_data, kSmiTagMask); 2204 __ Check(ne, kUnexpectedTypeForRegExpDataFixedArrayExpected); 2205 __ CompareObjectType(regexp_data, x10, x10, FIXED_ARRAY_TYPE); 2206 __ Check(eq, kUnexpectedTypeForRegExpDataFixedArrayExpected); 2207 } 2208 2209 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP. 2210 __ Ldr(x10, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset)); 2211 __ Cmp(x10, Smi::FromInt(JSRegExp::IRREGEXP)); 2212 __ B(ne, &runtime); 2213 2214 // Check that the number of captures fit in the static offsets vector buffer. 2215 // We have always at least one capture for the whole match, plus additional 2216 // ones due to capturing parentheses. A capture takes 2 registers. 2217 // The number of capture registers then is (number_of_captures + 1) * 2. 2218 __ Ldrsw(x10, 2219 UntagSmiFieldMemOperand(regexp_data, 2220 JSRegExp::kIrregexpCaptureCountOffset)); 2221 // Check (number_of_captures + 1) * 2 <= offsets vector size 2222 // number_of_captures * 2 <= offsets vector size - 2 2223 STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2); 2224 __ Add(x10, x10, x10); 2225 __ Cmp(x10, Isolate::kJSRegexpStaticOffsetsVectorSize - 2); 2226 __ B(hi, &runtime); 2227 2228 // Initialize offset for possibly sliced string. 2229 __ Mov(sliced_string_offset, 0); 2230 2231 DCHECK(jssp.Is(__ StackPointer())); 2232 __ Peek(subject, kSubjectOffset); 2233 __ JumpIfSmi(subject, &runtime); 2234 2235 __ Ldr(x10, FieldMemOperand(subject, HeapObject::kMapOffset)); 2236 __ Ldrb(string_type, FieldMemOperand(x10, Map::kInstanceTypeOffset)); 2237 2238 __ Ldr(jsstring_length, FieldMemOperand(subject, String::kLengthOffset)); 2239 2240 // Handle subject string according to its encoding and representation: 2241 // (1) Sequential string? If yes, go to (5). 2242 // (2) Anything but sequential or cons? If yes, go to (6). 2243 // (3) Cons string. If the string is flat, replace subject with first string. 2244 // Otherwise bailout. 2245 // (4) Is subject external? If yes, go to (7). 2246 // (5) Sequential string. Load regexp code according to encoding. 2247 // (E) Carry on. 2248 /// [...] 2249 2250 // Deferred code at the end of the stub: 2251 // (6) Not a long external string? If yes, go to (8). 2252 // (7) External string. Make it, offset-wise, look like a sequential string. 2253 // Go to (5). 2254 // (8) Short external string or not a string? If yes, bail out to runtime. 2255 // (9) Sliced string. Replace subject with parent. Go to (4). 2256 2257 Label check_underlying; // (4) 2258 Label seq_string; // (5) 2259 Label not_seq_nor_cons; // (6) 2260 Label external_string; // (7) 2261 Label not_long_external; // (8) 2262 2263 // (1) Sequential string? If yes, go to (5). 2264 __ And(string_representation, 2265 string_type, 2266 kIsNotStringMask | 2267 kStringRepresentationMask | 2268 kShortExternalStringMask); 2269 // We depend on the fact that Strings of type 2270 // SeqString and not ShortExternalString are defined 2271 // by the following pattern: 2272 // string_type: 0XX0 XX00 2273 // ^ ^ ^^ 2274 // | | || 2275 // | | is a SeqString 2276 // | is not a short external String 2277 // is a String 2278 STATIC_ASSERT((kStringTag | kSeqStringTag) == 0); 2279 STATIC_ASSERT(kShortExternalStringTag != 0); 2280 __ Cbz(string_representation, &seq_string); // Go to (5). 2281 2282 // (2) Anything but sequential or cons? If yes, go to (6). 2283 STATIC_ASSERT(kConsStringTag < kExternalStringTag); 2284 STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); 2285 STATIC_ASSERT(kIsNotStringMask > kExternalStringTag); 2286 STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag); 2287 __ Cmp(string_representation, kExternalStringTag); 2288 __ B(ge, ¬_seq_nor_cons); // Go to (6). 2289 2290 // (3) Cons string. Check that it's flat. 2291 __ Ldr(x10, FieldMemOperand(subject, ConsString::kSecondOffset)); 2292 __ JumpIfNotRoot(x10, Heap::kempty_stringRootIndex, &runtime); 2293 // Replace subject with first string. 2294 __ Ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset)); 2295 2296 // (4) Is subject external? If yes, go to (7). 2297 __ Bind(&check_underlying); 2298 // Reload the string type. 2299 __ Ldr(x10, FieldMemOperand(subject, HeapObject::kMapOffset)); 2300 __ Ldrb(string_type, FieldMemOperand(x10, Map::kInstanceTypeOffset)); 2301 STATIC_ASSERT(kSeqStringTag == 0); 2302 // The underlying external string is never a short external string. 2303 STATIC_ASSERT(ExternalString::kMaxShortLength < ConsString::kMinLength); 2304 STATIC_ASSERT(ExternalString::kMaxShortLength < SlicedString::kMinLength); 2305 __ TestAndBranchIfAnySet(string_type.X(), 2306 kStringRepresentationMask, 2307 &external_string); // Go to (7). 2308 2309 // (5) Sequential string. Load regexp code according to encoding. 2310 __ Bind(&seq_string); 2311 2312 // Check that the third argument is a positive smi less than the subject 2313 // string length. A negative value will be greater (unsigned comparison). 2314 DCHECK(jssp.Is(__ StackPointer())); 2315 __ Peek(x10, kPreviousIndexOffset); 2316 __ JumpIfNotSmi(x10, &runtime); 2317 __ Cmp(jsstring_length, x10); 2318 __ B(ls, &runtime); 2319 2320 // Argument 2 (x1): We need to load argument 2 (the previous index) into x1 2321 // before entering the exit frame. 2322 __ SmiUntag(x1, x10); 2323 2324 // The third bit determines the string encoding in string_type. 2325 STATIC_ASSERT(kOneByteStringTag == 0x04); 2326 STATIC_ASSERT(kTwoByteStringTag == 0x00); 2327 STATIC_ASSERT(kStringEncodingMask == 0x04); 2328 2329 // Find the code object based on the assumptions above. 2330 // kDataOneByteCodeOffset and kDataUC16CodeOffset are adjacent, adds an offset 2331 // of kPointerSize to reach the latter. 2332 DCHECK_EQ(JSRegExp::kDataOneByteCodeOffset + kPointerSize, 2333 JSRegExp::kDataUC16CodeOffset); 2334 __ Mov(x10, kPointerSize); 2335 // We will need the encoding later: Latin1 = 0x04 2336 // UC16 = 0x00 2337 __ Ands(string_encoding, string_type, kStringEncodingMask); 2338 __ CzeroX(x10, ne); 2339 __ Add(x10, regexp_data, x10); 2340 __ Ldr(code_object, FieldMemOperand(x10, JSRegExp::kDataOneByteCodeOffset)); 2341 2342 // (E) Carry on. String handling is done. 2343 2344 // Check that the irregexp code has been generated for the actual string 2345 // encoding. If it has, the field contains a code object otherwise it contains 2346 // a smi (code flushing support). 2347 __ JumpIfSmi(code_object, &runtime); 2348 2349 // All checks done. Now push arguments for native regexp code. 2350 __ IncrementCounter(isolate()->counters()->regexp_entry_native(), 1, 2351 x10, 2352 x11); 2353 2354 // Isolates: note we add an additional parameter here (isolate pointer). 2355 __ EnterExitFrame(false, x10, 1); 2356 DCHECK(csp.Is(__ StackPointer())); 2357 2358 // We have 9 arguments to pass to the regexp code, therefore we have to pass 2359 // one on the stack and the rest as registers. 2360 2361 // Note that the placement of the argument on the stack isn't standard 2362 // AAPCS64: 2363 // csp[0]: Space for the return address placed by DirectCEntryStub. 2364 // csp[8]: Argument 9, the current isolate address. 2365 2366 __ Mov(x10, ExternalReference::isolate_address(isolate())); 2367 __ Poke(x10, kPointerSize); 2368 2369 Register length = w11; 2370 Register previous_index_in_bytes = w12; 2371 Register start = x13; 2372 2373 // Load start of the subject string. 2374 __ Add(start, subject, SeqString::kHeaderSize - kHeapObjectTag); 2375 // Load the length from the original subject string from the previous stack 2376 // frame. Therefore we have to use fp, which points exactly to two pointer 2377 // sizes below the previous sp. (Because creating a new stack frame pushes 2378 // the previous fp onto the stack and decrements sp by 2 * kPointerSize.) 2379 __ Ldr(subject, MemOperand(fp, kSubjectOffset + 2 * kPointerSize)); 2380 __ Ldr(length, UntagSmiFieldMemOperand(subject, String::kLengthOffset)); 2381 2382 // Handle UC16 encoding, two bytes make one character. 2383 // string_encoding: if Latin1: 0x04 2384 // if UC16: 0x00 2385 STATIC_ASSERT(kStringEncodingMask == 0x04); 2386 __ Ubfx(string_encoding, string_encoding, 2, 1); 2387 __ Eor(string_encoding, string_encoding, 1); 2388 // string_encoding: if Latin1: 0 2389 // if UC16: 1 2390 2391 // Convert string positions from characters to bytes. 2392 // Previous index is in x1. 2393 __ Lsl(previous_index_in_bytes, w1, string_encoding); 2394 __ Lsl(length, length, string_encoding); 2395 __ Lsl(sliced_string_offset, sliced_string_offset, string_encoding); 2396 2397 // Argument 1 (x0): Subject string. 2398 __ Mov(x0, subject); 2399 2400 // Argument 2 (x1): Previous index, already there. 2401 2402 // Argument 3 (x2): Get the start of input. 2403 // Start of input = start of string + previous index + substring offset 2404 // (0 if the string 2405 // is not sliced). 2406 __ Add(w10, previous_index_in_bytes, sliced_string_offset); 2407 __ Add(x2, start, Operand(w10, UXTW)); 2408 2409 // Argument 4 (x3): 2410 // End of input = start of input + (length of input - previous index) 2411 __ Sub(w10, length, previous_index_in_bytes); 2412 __ Add(x3, x2, Operand(w10, UXTW)); 2413 2414 // Argument 5 (x4): static offsets vector buffer. 2415 __ Mov(x4, ExternalReference::address_of_static_offsets_vector(isolate())); 2416 2417 // Argument 6 (x5): Set the number of capture registers to zero to force 2418 // global regexps to behave as non-global. This stub is not used for global 2419 // regexps. 2420 __ Mov(x5, 0); 2421 2422 // Argument 7 (x6): Start (high end) of backtracking stack memory area. 2423 __ Mov(x10, address_of_regexp_stack_memory_address); 2424 __ Ldr(x10, MemOperand(x10)); 2425 __ Mov(x11, address_of_regexp_stack_memory_size); 2426 __ Ldr(x11, MemOperand(x11)); 2427 __ Add(x6, x10, x11); 2428 2429 // Argument 8 (x7): Indicate that this is a direct call from JavaScript. 2430 __ Mov(x7, 1); 2431 2432 // Locate the code entry and call it. 2433 __ Add(code_object, code_object, Code::kHeaderSize - kHeapObjectTag); 2434 DirectCEntryStub stub(isolate()); 2435 stub.GenerateCall(masm, code_object); 2436 2437 __ LeaveExitFrame(false, x10, true); 2438 2439 // The generated regexp code returns an int32 in w0. 2440 Label failure, exception; 2441 __ CompareAndBranch(w0, NativeRegExpMacroAssembler::FAILURE, eq, &failure); 2442 __ CompareAndBranch(w0, 2443 NativeRegExpMacroAssembler::EXCEPTION, 2444 eq, 2445 &exception); 2446 __ CompareAndBranch(w0, NativeRegExpMacroAssembler::RETRY, eq, &runtime); 2447 2448 // Success: process the result from the native regexp code. 2449 Register number_of_capture_registers = x12; 2450 2451 // Calculate number of capture registers (number_of_captures + 1) * 2 2452 // and store it in the last match info. 2453 __ Ldrsw(x10, 2454 UntagSmiFieldMemOperand(regexp_data, 2455 JSRegExp::kIrregexpCaptureCountOffset)); 2456 __ Add(x10, x10, x10); 2457 __ Add(number_of_capture_registers, x10, 2); 2458 2459 // Check that the fourth object is a JSArray object. 2460 DCHECK(jssp.Is(__ StackPointer())); 2461 __ Peek(x10, kLastMatchInfoOffset); 2462 __ JumpIfSmi(x10, &runtime); 2463 __ JumpIfNotObjectType(x10, x11, x11, JS_ARRAY_TYPE, &runtime); 2464 2465 // Check that the JSArray is the fast case. 2466 __ Ldr(last_match_info_elements, 2467 FieldMemOperand(x10, JSArray::kElementsOffset)); 2468 __ Ldr(x10, 2469 FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset)); 2470 __ JumpIfNotRoot(x10, Heap::kFixedArrayMapRootIndex, &runtime); 2471 2472 // Check that the last match info has space for the capture registers and the 2473 // additional information (overhead). 2474 // (number_of_captures + 1) * 2 + overhead <= last match info size 2475 // (number_of_captures * 2) + 2 + overhead <= last match info size 2476 // number_of_capture_registers + overhead <= last match info size 2477 __ Ldrsw(x10, 2478 UntagSmiFieldMemOperand(last_match_info_elements, 2479 FixedArray::kLengthOffset)); 2480 __ Add(x11, number_of_capture_registers, RegExpImpl::kLastMatchOverhead); 2481 __ Cmp(x11, x10); 2482 __ B(gt, &runtime); 2483 2484 // Store the capture count. 2485 __ SmiTag(x10, number_of_capture_registers); 2486 __ Str(x10, 2487 FieldMemOperand(last_match_info_elements, 2488 RegExpImpl::kLastCaptureCountOffset)); 2489 // Store last subject and last input. 2490 __ Str(subject, 2491 FieldMemOperand(last_match_info_elements, 2492 RegExpImpl::kLastSubjectOffset)); 2493 // Use x10 as the subject string in order to only need 2494 // one RecordWriteStub. 2495 __ Mov(x10, subject); 2496 __ RecordWriteField(last_match_info_elements, 2497 RegExpImpl::kLastSubjectOffset, 2498 x10, 2499 x11, 2500 kLRHasNotBeenSaved, 2501 kDontSaveFPRegs); 2502 __ Str(subject, 2503 FieldMemOperand(last_match_info_elements, 2504 RegExpImpl::kLastInputOffset)); 2505 __ Mov(x10, subject); 2506 __ RecordWriteField(last_match_info_elements, 2507 RegExpImpl::kLastInputOffset, 2508 x10, 2509 x11, 2510 kLRHasNotBeenSaved, 2511 kDontSaveFPRegs); 2512 2513 Register last_match_offsets = x13; 2514 Register offsets_vector_index = x14; 2515 Register current_offset = x15; 2516 2517 // Get the static offsets vector filled by the native regexp code 2518 // and fill the last match info. 2519 ExternalReference address_of_static_offsets_vector = 2520 ExternalReference::address_of_static_offsets_vector(isolate()); 2521 __ Mov(offsets_vector_index, address_of_static_offsets_vector); 2522 2523 Label next_capture, done; 2524 // Capture register counter starts from number of capture registers and 2525 // iterates down to zero (inclusive). 2526 __ Add(last_match_offsets, 2527 last_match_info_elements, 2528 RegExpImpl::kFirstCaptureOffset - kHeapObjectTag); 2529 __ Bind(&next_capture); 2530 __ Subs(number_of_capture_registers, number_of_capture_registers, 2); 2531 __ B(mi, &done); 2532 // Read two 32 bit values from the static offsets vector buffer into 2533 // an X register 2534 __ Ldr(current_offset, 2535 MemOperand(offsets_vector_index, kWRegSize * 2, PostIndex)); 2536 // Store the smi values in the last match info. 2537 __ SmiTag(x10, current_offset); 2538 // Clearing the 32 bottom bits gives us a Smi. 2539 STATIC_ASSERT(kSmiTag == 0); 2540 __ Bic(x11, current_offset, kSmiShiftMask); 2541 __ Stp(x10, 2542 x11, 2543 MemOperand(last_match_offsets, kXRegSize * 2, PostIndex)); 2544 __ B(&next_capture); 2545 __ Bind(&done); 2546 2547 // Return last match info. 2548 __ Peek(x0, kLastMatchInfoOffset); 2549 __ PopCPURegList(used_callee_saved_registers); 2550 // Drop the 4 arguments of the stub from the stack. 2551 __ Drop(4); 2552 __ Ret(); 2553 2554 __ Bind(&exception); 2555 Register exception_value = x0; 2556 // A stack overflow (on the backtrack stack) may have occured 2557 // in the RegExp code but no exception has been created yet. 2558 // If there is no pending exception, handle that in the runtime system. 2559 __ Mov(x10, Operand(isolate()->factory()->the_hole_value())); 2560 __ Mov(x11, 2561 Operand(ExternalReference(Isolate::kPendingExceptionAddress, 2562 isolate()))); 2563 __ Ldr(exception_value, MemOperand(x11)); 2564 __ Cmp(x10, exception_value); 2565 __ B(eq, &runtime); 2566 2567 __ Str(x10, MemOperand(x11)); // Clear pending exception. 2568 2569 // Check if the exception is a termination. If so, throw as uncatchable. 2570 Label termination_exception; 2571 __ JumpIfRoot(exception_value, 2572 Heap::kTerminationExceptionRootIndex, 2573 &termination_exception); 2574 2575 __ Throw(exception_value, x10, x11, x12, x13); 2576 2577 __ Bind(&termination_exception); 2578 __ ThrowUncatchable(exception_value, x10, x11, x12, x13); 2579 2580 __ Bind(&failure); 2581 __ Mov(x0, Operand(isolate()->factory()->null_value())); 2582 __ PopCPURegList(used_callee_saved_registers); 2583 // Drop the 4 arguments of the stub from the stack. 2584 __ Drop(4); 2585 __ Ret(); 2586 2587 __ Bind(&runtime); 2588 __ PopCPURegList(used_callee_saved_registers); 2589 __ TailCallRuntime(Runtime::kRegExpExecRT, 4, 1); 2590 2591 // Deferred code for string handling. 2592 // (6) Not a long external string? If yes, go to (8). 2593 __ Bind(¬_seq_nor_cons); 2594 // Compare flags are still set. 2595 __ B(ne, ¬_long_external); // Go to (8). 2596 2597 // (7) External string. Make it, offset-wise, look like a sequential string. 2598 __ Bind(&external_string); 2599 if (masm->emit_debug_code()) { 2600 // Assert that we do not have a cons or slice (indirect strings) here. 2601 // Sequential strings have already been ruled out. 2602 __ Ldr(x10, FieldMemOperand(subject, HeapObject::kMapOffset)); 2603 __ Ldrb(x10, FieldMemOperand(x10, Map::kInstanceTypeOffset)); 2604 __ Tst(x10, kIsIndirectStringMask); 2605 __ Check(eq, kExternalStringExpectedButNotFound); 2606 __ And(x10, x10, kStringRepresentationMask); 2607 __ Cmp(x10, 0); 2608 __ Check(ne, kExternalStringExpectedButNotFound); 2609 } 2610 __ Ldr(subject, 2611 FieldMemOperand(subject, ExternalString::kResourceDataOffset)); 2612 // Move the pointer so that offset-wise, it looks like a sequential string. 2613 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); 2614 __ Sub(subject, subject, SeqTwoByteString::kHeaderSize - kHeapObjectTag); 2615 __ B(&seq_string); // Go to (5). 2616 2617 // (8) If this is a short external string or not a string, bail out to 2618 // runtime. 2619 __ Bind(¬_long_external); 2620 STATIC_ASSERT(kShortExternalStringTag != 0); 2621 __ TestAndBranchIfAnySet(string_representation, 2622 kShortExternalStringMask | kIsNotStringMask, 2623 &runtime); 2624 2625 // (9) Sliced string. Replace subject with parent. 2626 __ Ldr(sliced_string_offset, 2627 UntagSmiFieldMemOperand(subject, SlicedString::kOffsetOffset)); 2628 __ Ldr(subject, FieldMemOperand(subject, SlicedString::kParentOffset)); 2629 __ B(&check_underlying); // Go to (4). 2630 #endif 2631 } 2632 2633 2634 static void GenerateRecordCallTarget(MacroAssembler* masm, 2635 Register argc, 2636 Register function, 2637 Register feedback_vector, 2638 Register index, 2639 Register scratch1, 2640 Register scratch2) { 2641 ASM_LOCATION("GenerateRecordCallTarget"); 2642 DCHECK(!AreAliased(scratch1, scratch2, 2643 argc, function, feedback_vector, index)); 2644 // Cache the called function in a feedback vector slot. Cache states are 2645 // uninitialized, monomorphic (indicated by a JSFunction), and megamorphic. 2646 // argc : number of arguments to the construct function 2647 // function : the function to call 2648 // feedback_vector : the feedback vector 2649 // index : slot in feedback vector (smi) 2650 Label initialize, done, miss, megamorphic, not_array_function; 2651 2652 DCHECK_EQ(*TypeFeedbackVector::MegamorphicSentinel(masm->isolate()), 2653 masm->isolate()->heap()->megamorphic_symbol()); 2654 DCHECK_EQ(*TypeFeedbackVector::UninitializedSentinel(masm->isolate()), 2655 masm->isolate()->heap()->uninitialized_symbol()); 2656 2657 // Load the cache state. 2658 __ Add(scratch1, feedback_vector, 2659 Operand::UntagSmiAndScale(index, kPointerSizeLog2)); 2660 __ Ldr(scratch1, FieldMemOperand(scratch1, FixedArray::kHeaderSize)); 2661 2662 // A monomorphic cache hit or an already megamorphic state: invoke the 2663 // function without changing the state. 2664 __ Cmp(scratch1, function); 2665 __ B(eq, &done); 2666 2667 if (!FLAG_pretenuring_call_new) { 2668 // If we came here, we need to see if we are the array function. 2669 // If we didn't have a matching function, and we didn't find the megamorph 2670 // sentinel, then we have in the slot either some other function or an 2671 // AllocationSite. Do a map check on the object in scratch1 register. 2672 __ Ldr(scratch2, FieldMemOperand(scratch1, AllocationSite::kMapOffset)); 2673 __ JumpIfNotRoot(scratch2, Heap::kAllocationSiteMapRootIndex, &miss); 2674 2675 // Make sure the function is the Array() function 2676 __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, scratch1); 2677 __ Cmp(function, scratch1); 2678 __ B(ne, &megamorphic); 2679 __ B(&done); 2680 } 2681 2682 __ Bind(&miss); 2683 2684 // A monomorphic miss (i.e, here the cache is not uninitialized) goes 2685 // megamorphic. 2686 __ JumpIfRoot(scratch1, Heap::kUninitializedSymbolRootIndex, &initialize); 2687 // MegamorphicSentinel is an immortal immovable object (undefined) so no 2688 // write-barrier is needed. 2689 __ Bind(&megamorphic); 2690 __ Add(scratch1, feedback_vector, 2691 Operand::UntagSmiAndScale(index, kPointerSizeLog2)); 2692 __ LoadRoot(scratch2, Heap::kMegamorphicSymbolRootIndex); 2693 __ Str(scratch2, FieldMemOperand(scratch1, FixedArray::kHeaderSize)); 2694 __ B(&done); 2695 2696 // An uninitialized cache is patched with the function or sentinel to 2697 // indicate the ElementsKind if function is the Array constructor. 2698 __ Bind(&initialize); 2699 2700 if (!FLAG_pretenuring_call_new) { 2701 // Make sure the function is the Array() function 2702 __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, scratch1); 2703 __ Cmp(function, scratch1); 2704 __ B(ne, ¬_array_function); 2705 2706 // The target function is the Array constructor, 2707 // Create an AllocationSite if we don't already have it, store it in the 2708 // slot. 2709 { 2710 FrameScope scope(masm, StackFrame::INTERNAL); 2711 CreateAllocationSiteStub create_stub(masm->isolate()); 2712 2713 // Arguments register must be smi-tagged to call out. 2714 __ SmiTag(argc); 2715 __ Push(argc, function, feedback_vector, index); 2716 2717 // CreateAllocationSiteStub expect the feedback vector in x2 and the slot 2718 // index in x3. 2719 DCHECK(feedback_vector.Is(x2) && index.Is(x3)); 2720 __ CallStub(&create_stub); 2721 2722 __ Pop(index, feedback_vector, function, argc); 2723 __ SmiUntag(argc); 2724 } 2725 __ B(&done); 2726 2727 __ Bind(¬_array_function); 2728 } 2729 2730 // An uninitialized cache is patched with the function. 2731 2732 __ Add(scratch1, feedback_vector, 2733 Operand::UntagSmiAndScale(index, kPointerSizeLog2)); 2734 __ Add(scratch1, scratch1, FixedArray::kHeaderSize - kHeapObjectTag); 2735 __ Str(function, MemOperand(scratch1, 0)); 2736 2737 __ Push(function); 2738 __ RecordWrite(feedback_vector, scratch1, function, kLRHasNotBeenSaved, 2739 kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); 2740 __ Pop(function); 2741 2742 __ Bind(&done); 2743 } 2744 2745 2746 static void EmitContinueIfStrictOrNative(MacroAssembler* masm, Label* cont) { 2747 // Do not transform the receiver for strict mode functions. 2748 __ Ldr(x3, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); 2749 __ Ldr(w4, FieldMemOperand(x3, SharedFunctionInfo::kCompilerHintsOffset)); 2750 __ Tbnz(w4, SharedFunctionInfo::kStrictModeFunction, cont); 2751 2752 // Do not transform the receiver for native (Compilerhints already in x3). 2753 __ Tbnz(w4, SharedFunctionInfo::kNative, cont); 2754 } 2755 2756 2757 static void EmitSlowCase(MacroAssembler* masm, 2758 int argc, 2759 Register function, 2760 Register type, 2761 Label* non_function) { 2762 // Check for function proxy. 2763 // x10 : function type. 2764 __ CompareAndBranch(type, JS_FUNCTION_PROXY_TYPE, ne, non_function); 2765 __ Push(function); // put proxy as additional argument 2766 __ Mov(x0, argc + 1); 2767 __ Mov(x2, 0); 2768 __ GetBuiltinFunction(x1, Builtins::CALL_FUNCTION_PROXY); 2769 { 2770 Handle<Code> adaptor = 2771 masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(); 2772 __ Jump(adaptor, RelocInfo::CODE_TARGET); 2773 } 2774 2775 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead 2776 // of the original receiver from the call site). 2777 __ Bind(non_function); 2778 __ Poke(function, argc * kXRegSize); 2779 __ Mov(x0, argc); // Set up the number of arguments. 2780 __ Mov(x2, 0); 2781 __ GetBuiltinFunction(function, Builtins::CALL_NON_FUNCTION); 2782 __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), 2783 RelocInfo::CODE_TARGET); 2784 } 2785 2786 2787 static void EmitWrapCase(MacroAssembler* masm, int argc, Label* cont) { 2788 // Wrap the receiver and patch it back onto the stack. 2789 { FrameScope frame_scope(masm, StackFrame::INTERNAL); 2790 __ Push(x1, x3); 2791 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); 2792 __ Pop(x1); 2793 } 2794 __ Poke(x0, argc * kPointerSize); 2795 __ B(cont); 2796 } 2797 2798 2799 static void CallFunctionNoFeedback(MacroAssembler* masm, 2800 int argc, bool needs_checks, 2801 bool call_as_method) { 2802 // x1 function the function to call 2803 Register function = x1; 2804 Register type = x4; 2805 Label slow, non_function, wrap, cont; 2806 2807 // TODO(jbramley): This function has a lot of unnamed registers. Name them, 2808 // and tidy things up a bit. 2809 2810 if (needs_checks) { 2811 // Check that the function is really a JavaScript function. 2812 __ JumpIfSmi(function, &non_function); 2813 2814 // Goto slow case if we do not have a function. 2815 __ JumpIfNotObjectType(function, x10, type, JS_FUNCTION_TYPE, &slow); 2816 } 2817 2818 // Fast-case: Invoke the function now. 2819 // x1 function pushed function 2820 ParameterCount actual(argc); 2821 2822 if (call_as_method) { 2823 if (needs_checks) { 2824 EmitContinueIfStrictOrNative(masm, &cont); 2825 } 2826 2827 // Compute the receiver in sloppy mode. 2828 __ Peek(x3, argc * kPointerSize); 2829 2830 if (needs_checks) { 2831 __ JumpIfSmi(x3, &wrap); 2832 __ JumpIfObjectType(x3, x10, type, FIRST_SPEC_OBJECT_TYPE, &wrap, lt); 2833 } else { 2834 __ B(&wrap); 2835 } 2836 2837 __ Bind(&cont); 2838 } 2839 2840 __ InvokeFunction(function, 2841 actual, 2842 JUMP_FUNCTION, 2843 NullCallWrapper()); 2844 if (needs_checks) { 2845 // Slow-case: Non-function called. 2846 __ Bind(&slow); 2847 EmitSlowCase(masm, argc, function, type, &non_function); 2848 } 2849 2850 if (call_as_method) { 2851 __ Bind(&wrap); 2852 EmitWrapCase(masm, argc, &cont); 2853 } 2854 } 2855 2856 2857 void CallFunctionStub::Generate(MacroAssembler* masm) { 2858 ASM_LOCATION("CallFunctionStub::Generate"); 2859 CallFunctionNoFeedback(masm, argc(), NeedsChecks(), CallAsMethod()); 2860 } 2861 2862 2863 void CallConstructStub::Generate(MacroAssembler* masm) { 2864 ASM_LOCATION("CallConstructStub::Generate"); 2865 // x0 : number of arguments 2866 // x1 : the function to call 2867 // x2 : feedback vector 2868 // x3 : slot in feedback vector (smi) (if r2 is not the megamorphic symbol) 2869 Register function = x1; 2870 Label slow, non_function_call; 2871 2872 // Check that the function is not a smi. 2873 __ JumpIfSmi(function, &non_function_call); 2874 // Check that the function is a JSFunction. 2875 Register object_type = x10; 2876 __ JumpIfNotObjectType(function, object_type, object_type, JS_FUNCTION_TYPE, 2877 &slow); 2878 2879 if (RecordCallTarget()) { 2880 GenerateRecordCallTarget(masm, x0, function, x2, x3, x4, x5); 2881 2882 __ Add(x5, x2, Operand::UntagSmiAndScale(x3, kPointerSizeLog2)); 2883 if (FLAG_pretenuring_call_new) { 2884 // Put the AllocationSite from the feedback vector into x2. 2885 // By adding kPointerSize we encode that we know the AllocationSite 2886 // entry is at the feedback vector slot given by x3 + 1. 2887 __ Ldr(x2, FieldMemOperand(x5, FixedArray::kHeaderSize + kPointerSize)); 2888 } else { 2889 Label feedback_register_initialized; 2890 // Put the AllocationSite from the feedback vector into x2, or undefined. 2891 __ Ldr(x2, FieldMemOperand(x5, FixedArray::kHeaderSize)); 2892 __ Ldr(x5, FieldMemOperand(x2, AllocationSite::kMapOffset)); 2893 __ JumpIfRoot(x5, Heap::kAllocationSiteMapRootIndex, 2894 &feedback_register_initialized); 2895 __ LoadRoot(x2, Heap::kUndefinedValueRootIndex); 2896 __ bind(&feedback_register_initialized); 2897 } 2898 2899 __ AssertUndefinedOrAllocationSite(x2, x5); 2900 } 2901 2902 // Jump to the function-specific construct stub. 2903 Register jump_reg = x4; 2904 Register shared_func_info = jump_reg; 2905 Register cons_stub = jump_reg; 2906 Register cons_stub_code = jump_reg; 2907 __ Ldr(shared_func_info, 2908 FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); 2909 __ Ldr(cons_stub, 2910 FieldMemOperand(shared_func_info, 2911 SharedFunctionInfo::kConstructStubOffset)); 2912 __ Add(cons_stub_code, cons_stub, Code::kHeaderSize - kHeapObjectTag); 2913 __ Br(cons_stub_code); 2914 2915 Label do_call; 2916 __ Bind(&slow); 2917 __ Cmp(object_type, JS_FUNCTION_PROXY_TYPE); 2918 __ B(ne, &non_function_call); 2919 __ GetBuiltinFunction(x1, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR); 2920 __ B(&do_call); 2921 2922 __ Bind(&non_function_call); 2923 __ GetBuiltinFunction(x1, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); 2924 2925 __ Bind(&do_call); 2926 // Set expected number of arguments to zero (not changing x0). 2927 __ Mov(x2, 0); 2928 __ Jump(isolate()->builtins()->ArgumentsAdaptorTrampoline(), 2929 RelocInfo::CODE_TARGET); 2930 } 2931 2932 2933 static void EmitLoadTypeFeedbackVector(MacroAssembler* masm, Register vector) { 2934 __ Ldr(vector, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 2935 __ Ldr(vector, FieldMemOperand(vector, 2936 JSFunction::kSharedFunctionInfoOffset)); 2937 __ Ldr(vector, FieldMemOperand(vector, 2938 SharedFunctionInfo::kFeedbackVectorOffset)); 2939 } 2940 2941 2942 void CallIC_ArrayStub::Generate(MacroAssembler* masm) { 2943 // x1 - function 2944 // x3 - slot id 2945 Label miss; 2946 Register function = x1; 2947 Register feedback_vector = x2; 2948 Register index = x3; 2949 Register scratch = x4; 2950 2951 EmitLoadTypeFeedbackVector(masm, feedback_vector); 2952 2953 __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, scratch); 2954 __ Cmp(function, scratch); 2955 __ B(ne, &miss); 2956 2957 __ Mov(x0, Operand(arg_count())); 2958 2959 __ Add(scratch, feedback_vector, 2960 Operand::UntagSmiAndScale(index, kPointerSizeLog2)); 2961 __ Ldr(scratch, FieldMemOperand(scratch, FixedArray::kHeaderSize)); 2962 2963 // Verify that scratch contains an AllocationSite 2964 Register map = x5; 2965 __ Ldr(map, FieldMemOperand(scratch, HeapObject::kMapOffset)); 2966 __ JumpIfNotRoot(map, Heap::kAllocationSiteMapRootIndex, &miss); 2967 2968 Register allocation_site = feedback_vector; 2969 __ Mov(allocation_site, scratch); 2970 ArrayConstructorStub stub(masm->isolate(), arg_count()); 2971 __ TailCallStub(&stub); 2972 2973 __ bind(&miss); 2974 GenerateMiss(masm); 2975 2976 // The slow case, we need this no matter what to complete a call after a miss. 2977 CallFunctionNoFeedback(masm, 2978 arg_count(), 2979 true, 2980 CallAsMethod()); 2981 2982 __ Unreachable(); 2983 } 2984 2985 2986 void CallICStub::Generate(MacroAssembler* masm) { 2987 ASM_LOCATION("CallICStub"); 2988 2989 // x1 - function 2990 // x3 - slot id (Smi) 2991 Label extra_checks_or_miss, slow_start; 2992 Label slow, non_function, wrap, cont; 2993 Label have_js_function; 2994 int argc = arg_count(); 2995 ParameterCount actual(argc); 2996 2997 Register function = x1; 2998 Register feedback_vector = x2; 2999 Register index = x3; 3000 Register type = x4; 3001 3002 EmitLoadTypeFeedbackVector(masm, feedback_vector); 3003 3004 // The checks. First, does x1 match the recorded monomorphic target? 3005 __ Add(x4, feedback_vector, 3006 Operand::UntagSmiAndScale(index, kPointerSizeLog2)); 3007 __ Ldr(x4, FieldMemOperand(x4, FixedArray::kHeaderSize)); 3008 3009 __ Cmp(x4, function); 3010 __ B(ne, &extra_checks_or_miss); 3011 3012 __ bind(&have_js_function); 3013 if (CallAsMethod()) { 3014 EmitContinueIfStrictOrNative(masm, &cont); 3015 3016 // Compute the receiver in sloppy mode. 3017 __ Peek(x3, argc * kPointerSize); 3018 3019 __ JumpIfSmi(x3, &wrap); 3020 __ JumpIfObjectType(x3, x10, type, FIRST_SPEC_OBJECT_TYPE, &wrap, lt); 3021 3022 __ Bind(&cont); 3023 } 3024 3025 __ InvokeFunction(function, 3026 actual, 3027 JUMP_FUNCTION, 3028 NullCallWrapper()); 3029 3030 __ bind(&slow); 3031 EmitSlowCase(masm, argc, function, type, &non_function); 3032 3033 if (CallAsMethod()) { 3034 __ bind(&wrap); 3035 EmitWrapCase(masm, argc, &cont); 3036 } 3037 3038 __ bind(&extra_checks_or_miss); 3039 Label miss; 3040 3041 __ JumpIfRoot(x4, Heap::kMegamorphicSymbolRootIndex, &slow_start); 3042 __ JumpIfRoot(x4, Heap::kUninitializedSymbolRootIndex, &miss); 3043 3044 if (!FLAG_trace_ic) { 3045 // We are going megamorphic. If the feedback is a JSFunction, it is fine 3046 // to handle it here. More complex cases are dealt with in the runtime. 3047 __ AssertNotSmi(x4); 3048 __ JumpIfNotObjectType(x4, x5, x5, JS_FUNCTION_TYPE, &miss); 3049 __ Add(x4, feedback_vector, 3050 Operand::UntagSmiAndScale(index, kPointerSizeLog2)); 3051 __ LoadRoot(x5, Heap::kMegamorphicSymbolRootIndex); 3052 __ Str(x5, FieldMemOperand(x4, FixedArray::kHeaderSize)); 3053 __ B(&slow_start); 3054 } 3055 3056 // We are here because tracing is on or we are going monomorphic. 3057 __ bind(&miss); 3058 GenerateMiss(masm); 3059 3060 // the slow case 3061 __ bind(&slow_start); 3062 3063 // Check that the function is really a JavaScript function. 3064 __ JumpIfSmi(function, &non_function); 3065 3066 // Goto slow case if we do not have a function. 3067 __ JumpIfNotObjectType(function, x10, type, JS_FUNCTION_TYPE, &slow); 3068 __ B(&have_js_function); 3069 } 3070 3071 3072 void CallICStub::GenerateMiss(MacroAssembler* masm) { 3073 ASM_LOCATION("CallICStub[Miss]"); 3074 3075 // Get the receiver of the function from the stack; 1 ~ return address. 3076 __ Peek(x4, (arg_count() + 1) * kPointerSize); 3077 3078 { 3079 FrameScope scope(masm, StackFrame::INTERNAL); 3080 3081 // Push the receiver and the function and feedback info. 3082 __ Push(x4, x1, x2, x3); 3083 3084 // Call the entry. 3085 IC::UtilityId id = GetICState() == DEFAULT ? IC::kCallIC_Miss 3086 : IC::kCallIC_Customization_Miss; 3087 3088 ExternalReference miss = ExternalReference(IC_Utility(id), 3089 masm->isolate()); 3090 __ CallExternalReference(miss, 4); 3091 3092 // Move result to edi and exit the internal frame. 3093 __ Mov(x1, x0); 3094 } 3095 } 3096 3097 3098 void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { 3099 // If the receiver is a smi trigger the non-string case. 3100 __ JumpIfSmi(object_, receiver_not_string_); 3101 3102 // Fetch the instance type of the receiver into result register. 3103 __ Ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset)); 3104 __ Ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset)); 3105 3106 // If the receiver is not a string trigger the non-string case. 3107 __ TestAndBranchIfAnySet(result_, kIsNotStringMask, receiver_not_string_); 3108 3109 // If the index is non-smi trigger the non-smi case. 3110 __ JumpIfNotSmi(index_, &index_not_smi_); 3111 3112 __ Bind(&got_smi_index_); 3113 // Check for index out of range. 3114 __ Ldrsw(result_, UntagSmiFieldMemOperand(object_, String::kLengthOffset)); 3115 __ Cmp(result_, Operand::UntagSmi(index_)); 3116 __ B(ls, index_out_of_range_); 3117 3118 __ SmiUntag(index_); 3119 3120 StringCharLoadGenerator::Generate(masm, 3121 object_, 3122 index_.W(), 3123 result_, 3124 &call_runtime_); 3125 __ SmiTag(result_); 3126 __ Bind(&exit_); 3127 } 3128 3129 3130 void StringCharCodeAtGenerator::GenerateSlow( 3131 MacroAssembler* masm, 3132 const RuntimeCallHelper& call_helper) { 3133 __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase); 3134 3135 __ Bind(&index_not_smi_); 3136 // If index is a heap number, try converting it to an integer. 3137 __ JumpIfNotHeapNumber(index_, index_not_number_); 3138 call_helper.BeforeCall(masm); 3139 // Save object_ on the stack and pass index_ as argument for runtime call. 3140 __ Push(object_, index_); 3141 if (index_flags_ == STRING_INDEX_IS_NUMBER) { 3142 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1); 3143 } else { 3144 DCHECK(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX); 3145 // NumberToSmi discards numbers that are not exact integers. 3146 __ CallRuntime(Runtime::kNumberToSmi, 1); 3147 } 3148 // Save the conversion result before the pop instructions below 3149 // have a chance to overwrite it. 3150 __ Mov(index_, x0); 3151 __ Pop(object_); 3152 // Reload the instance type. 3153 __ Ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset)); 3154 __ Ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset)); 3155 call_helper.AfterCall(masm); 3156 3157 // If index is still not a smi, it must be out of range. 3158 __ JumpIfNotSmi(index_, index_out_of_range_); 3159 // Otherwise, return to the fast path. 3160 __ B(&got_smi_index_); 3161 3162 // Call runtime. We get here when the receiver is a string and the 3163 // index is a number, but the code of getting the actual character 3164 // is too complex (e.g., when the string needs to be flattened). 3165 __ Bind(&call_runtime_); 3166 call_helper.BeforeCall(masm); 3167 __ SmiTag(index_); 3168 __ Push(object_, index_); 3169 __ CallRuntime(Runtime::kStringCharCodeAtRT, 2); 3170 __ Mov(result_, x0); 3171 call_helper.AfterCall(masm); 3172 __ B(&exit_); 3173 3174 __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase); 3175 } 3176 3177 3178 void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) { 3179 __ JumpIfNotSmi(code_, &slow_case_); 3180 __ Cmp(code_, Smi::FromInt(String::kMaxOneByteCharCode)); 3181 __ B(hi, &slow_case_); 3182 3183 __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex); 3184 // At this point code register contains smi tagged one-byte char code. 3185 __ Add(result_, result_, Operand::UntagSmiAndScale(code_, kPointerSizeLog2)); 3186 __ Ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize)); 3187 __ JumpIfRoot(result_, Heap::kUndefinedValueRootIndex, &slow_case_); 3188 __ Bind(&exit_); 3189 } 3190 3191 3192 void StringCharFromCodeGenerator::GenerateSlow( 3193 MacroAssembler* masm, 3194 const RuntimeCallHelper& call_helper) { 3195 __ Abort(kUnexpectedFallthroughToCharFromCodeSlowCase); 3196 3197 __ Bind(&slow_case_); 3198 call_helper.BeforeCall(masm); 3199 __ Push(code_); 3200 __ CallRuntime(Runtime::kCharFromCode, 1); 3201 __ Mov(result_, x0); 3202 call_helper.AfterCall(masm); 3203 __ B(&exit_); 3204 3205 __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase); 3206 } 3207 3208 3209 void CompareICStub::GenerateSmis(MacroAssembler* masm) { 3210 // Inputs are in x0 (lhs) and x1 (rhs). 3211 DCHECK(state() == CompareICState::SMI); 3212 ASM_LOCATION("CompareICStub[Smis]"); 3213 Label miss; 3214 // Bail out (to 'miss') unless both x0 and x1 are smis. 3215 __ JumpIfEitherNotSmi(x0, x1, &miss); 3216 3217 if (GetCondition() == eq) { 3218 // For equality we do not care about the sign of the result. 3219 __ Sub(x0, x0, x1); 3220 } else { 3221 // Untag before subtracting to avoid handling overflow. 3222 __ SmiUntag(x1); 3223 __ Sub(x0, x1, Operand::UntagSmi(x0)); 3224 } 3225 __ Ret(); 3226 3227 __ Bind(&miss); 3228 GenerateMiss(masm); 3229 } 3230 3231 3232 void CompareICStub::GenerateNumbers(MacroAssembler* masm) { 3233 DCHECK(state() == CompareICState::NUMBER); 3234 ASM_LOCATION("CompareICStub[HeapNumbers]"); 3235 3236 Label unordered, maybe_undefined1, maybe_undefined2; 3237 Label miss, handle_lhs, values_in_d_regs; 3238 Label untag_rhs, untag_lhs; 3239 3240 Register result = x0; 3241 Register rhs = x0; 3242 Register lhs = x1; 3243 FPRegister rhs_d = d0; 3244 FPRegister lhs_d = d1; 3245 3246 if (left() == CompareICState::SMI) { 3247 __ JumpIfNotSmi(lhs, &miss); 3248 } 3249 if (right() == CompareICState::SMI) { 3250 __ JumpIfNotSmi(rhs, &miss); 3251 } 3252 3253 __ SmiUntagToDouble(rhs_d, rhs, kSpeculativeUntag); 3254 __ SmiUntagToDouble(lhs_d, lhs, kSpeculativeUntag); 3255 3256 // Load rhs if it's a heap number. 3257 __ JumpIfSmi(rhs, &handle_lhs); 3258 __ JumpIfNotHeapNumber(rhs, &maybe_undefined1); 3259 __ Ldr(rhs_d, FieldMemOperand(rhs, HeapNumber::kValueOffset)); 3260 3261 // Load lhs if it's a heap number. 3262 __ Bind(&handle_lhs); 3263 __ JumpIfSmi(lhs, &values_in_d_regs); 3264 __ JumpIfNotHeapNumber(lhs, &maybe_undefined2); 3265 __ Ldr(lhs_d, FieldMemOperand(lhs, HeapNumber::kValueOffset)); 3266 3267 __ Bind(&values_in_d_regs); 3268 __ Fcmp(lhs_d, rhs_d); 3269 __ B(vs, &unordered); // Overflow flag set if either is NaN. 3270 STATIC_ASSERT((LESS == -1) && (EQUAL == 0) && (GREATER == 1)); 3271 __ Cset(result, gt); // gt => 1, otherwise (lt, eq) => 0 (EQUAL). 3272 __ Csinv(result, result, xzr, ge); // lt => -1, gt => 1, eq => 0. 3273 __ Ret(); 3274 3275 __ Bind(&unordered); 3276 CompareICStub stub(isolate(), op(), CompareICState::GENERIC, 3277 CompareICState::GENERIC, CompareICState::GENERIC); 3278 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); 3279 3280 __ Bind(&maybe_undefined1); 3281 if (Token::IsOrderedRelationalCompareOp(op())) { 3282 __ JumpIfNotRoot(rhs, Heap::kUndefinedValueRootIndex, &miss); 3283 __ JumpIfSmi(lhs, &unordered); 3284 __ JumpIfNotHeapNumber(lhs, &maybe_undefined2); 3285 __ B(&unordered); 3286 } 3287 3288 __ Bind(&maybe_undefined2); 3289 if (Token::IsOrderedRelationalCompareOp(op())) { 3290 __ JumpIfRoot(lhs, Heap::kUndefinedValueRootIndex, &unordered); 3291 } 3292 3293 __ Bind(&miss); 3294 GenerateMiss(masm); 3295 } 3296 3297 3298 void CompareICStub::GenerateInternalizedStrings(MacroAssembler* masm) { 3299 DCHECK(state() == CompareICState::INTERNALIZED_STRING); 3300 ASM_LOCATION("CompareICStub[InternalizedStrings]"); 3301 Label miss; 3302 3303 Register result = x0; 3304 Register rhs = x0; 3305 Register lhs = x1; 3306 3307 // Check that both operands are heap objects. 3308 __ JumpIfEitherSmi(lhs, rhs, &miss); 3309 3310 // Check that both operands are internalized strings. 3311 Register rhs_map = x10; 3312 Register lhs_map = x11; 3313 Register rhs_type = x10; 3314 Register lhs_type = x11; 3315 __ Ldr(lhs_map, FieldMemOperand(lhs, HeapObject::kMapOffset)); 3316 __ Ldr(rhs_map, FieldMemOperand(rhs, HeapObject::kMapOffset)); 3317 __ Ldrb(lhs_type, FieldMemOperand(lhs_map, Map::kInstanceTypeOffset)); 3318 __ Ldrb(rhs_type, FieldMemOperand(rhs_map, Map::kInstanceTypeOffset)); 3319 3320 STATIC_ASSERT((kInternalizedTag == 0) && (kStringTag == 0)); 3321 __ Orr(x12, lhs_type, rhs_type); 3322 __ TestAndBranchIfAnySet( 3323 x12, kIsNotStringMask | kIsNotInternalizedMask, &miss); 3324 3325 // Internalized strings are compared by identity. 3326 STATIC_ASSERT(EQUAL == 0); 3327 __ Cmp(lhs, rhs); 3328 __ Cset(result, ne); 3329 __ Ret(); 3330 3331 __ Bind(&miss); 3332 GenerateMiss(masm); 3333 } 3334 3335 3336 void CompareICStub::GenerateUniqueNames(MacroAssembler* masm) { 3337 DCHECK(state() == CompareICState::UNIQUE_NAME); 3338 ASM_LOCATION("CompareICStub[UniqueNames]"); 3339 DCHECK(GetCondition() == eq); 3340 Label miss; 3341 3342 Register result = x0; 3343 Register rhs = x0; 3344 Register lhs = x1; 3345 3346 Register lhs_instance_type = w2; 3347 Register rhs_instance_type = w3; 3348 3349 // Check that both operands are heap objects. 3350 __ JumpIfEitherSmi(lhs, rhs, &miss); 3351 3352 // Check that both operands are unique names. This leaves the instance 3353 // types loaded in tmp1 and tmp2. 3354 __ Ldr(x10, FieldMemOperand(lhs, HeapObject::kMapOffset)); 3355 __ Ldr(x11, FieldMemOperand(rhs, HeapObject::kMapOffset)); 3356 __ Ldrb(lhs_instance_type, FieldMemOperand(x10, Map::kInstanceTypeOffset)); 3357 __ Ldrb(rhs_instance_type, FieldMemOperand(x11, Map::kInstanceTypeOffset)); 3358 3359 // To avoid a miss, each instance type should be either SYMBOL_TYPE or it 3360 // should have kInternalizedTag set. 3361 __ JumpIfNotUniqueNameInstanceType(lhs_instance_type, &miss); 3362 __ JumpIfNotUniqueNameInstanceType(rhs_instance_type, &miss); 3363 3364 // Unique names are compared by identity. 3365 STATIC_ASSERT(EQUAL == 0); 3366 __ Cmp(lhs, rhs); 3367 __ Cset(result, ne); 3368 __ Ret(); 3369 3370 __ Bind(&miss); 3371 GenerateMiss(masm); 3372 } 3373 3374 3375 void CompareICStub::GenerateStrings(MacroAssembler* masm) { 3376 DCHECK(state() == CompareICState::STRING); 3377 ASM_LOCATION("CompareICStub[Strings]"); 3378 3379 Label miss; 3380 3381 bool equality = Token::IsEqualityOp(op()); 3382 3383 Register result = x0; 3384 Register rhs = x0; 3385 Register lhs = x1; 3386 3387 // Check that both operands are heap objects. 3388 __ JumpIfEitherSmi(rhs, lhs, &miss); 3389 3390 // Check that both operands are strings. 3391 Register rhs_map = x10; 3392 Register lhs_map = x11; 3393 Register rhs_type = x10; 3394 Register lhs_type = x11; 3395 __ Ldr(lhs_map, FieldMemOperand(lhs, HeapObject::kMapOffset)); 3396 __ Ldr(rhs_map, FieldMemOperand(rhs, HeapObject::kMapOffset)); 3397 __ Ldrb(lhs_type, FieldMemOperand(lhs_map, Map::kInstanceTypeOffset)); 3398 __ Ldrb(rhs_type, FieldMemOperand(rhs_map, Map::kInstanceTypeOffset)); 3399 STATIC_ASSERT(kNotStringTag != 0); 3400 __ Orr(x12, lhs_type, rhs_type); 3401 __ Tbnz(x12, MaskToBit(kIsNotStringMask), &miss); 3402 3403 // Fast check for identical strings. 3404 Label not_equal; 3405 __ Cmp(lhs, rhs); 3406 __ B(ne, ¬_equal); 3407 __ Mov(result, EQUAL); 3408 __ Ret(); 3409 3410 __ Bind(¬_equal); 3411 // Handle not identical strings 3412 3413 // Check that both strings are internalized strings. If they are, we're done 3414 // because we already know they are not identical. We know they are both 3415 // strings. 3416 if (equality) { 3417 DCHECK(GetCondition() == eq); 3418 STATIC_ASSERT(kInternalizedTag == 0); 3419 Label not_internalized_strings; 3420 __ Orr(x12, lhs_type, rhs_type); 3421 __ TestAndBranchIfAnySet( 3422 x12, kIsNotInternalizedMask, ¬_internalized_strings); 3423 // Result is in rhs (x0), and not EQUAL, as rhs is not a smi. 3424 __ Ret(); 3425 __ Bind(¬_internalized_strings); 3426 } 3427 3428 // Check that both strings are sequential one-byte. 3429 Label runtime; 3430 __ JumpIfBothInstanceTypesAreNotSequentialOneByte(lhs_type, rhs_type, x12, 3431 x13, &runtime); 3432 3433 // Compare flat one-byte strings. Returns when done. 3434 if (equality) { 3435 StringHelper::GenerateFlatOneByteStringEquals(masm, lhs, rhs, x10, x11, 3436 x12); 3437 } else { 3438 StringHelper::GenerateCompareFlatOneByteStrings(masm, lhs, rhs, x10, x11, 3439 x12, x13); 3440 } 3441 3442 // Handle more complex cases in runtime. 3443 __ Bind(&runtime); 3444 __ Push(lhs, rhs); 3445 if (equality) { 3446 __ TailCallRuntime(Runtime::kStringEquals, 2, 1); 3447 } else { 3448 __ TailCallRuntime(Runtime::kStringCompare, 2, 1); 3449 } 3450 3451 __ Bind(&miss); 3452 GenerateMiss(masm); 3453 } 3454 3455 3456 void CompareICStub::GenerateObjects(MacroAssembler* masm) { 3457 DCHECK(state() == CompareICState::OBJECT); 3458 ASM_LOCATION("CompareICStub[Objects]"); 3459 3460 Label miss; 3461 3462 Register result = x0; 3463 Register rhs = x0; 3464 Register lhs = x1; 3465 3466 __ JumpIfEitherSmi(rhs, lhs, &miss); 3467 3468 __ JumpIfNotObjectType(rhs, x10, x10, JS_OBJECT_TYPE, &miss); 3469 __ JumpIfNotObjectType(lhs, x10, x10, JS_OBJECT_TYPE, &miss); 3470 3471 DCHECK(GetCondition() == eq); 3472 __ Sub(result, rhs, lhs); 3473 __ Ret(); 3474 3475 __ Bind(&miss); 3476 GenerateMiss(masm); 3477 } 3478 3479 3480 void CompareICStub::GenerateKnownObjects(MacroAssembler* masm) { 3481 ASM_LOCATION("CompareICStub[KnownObjects]"); 3482 3483 Label miss; 3484 3485 Register result = x0; 3486 Register rhs = x0; 3487 Register lhs = x1; 3488 3489 __ JumpIfEitherSmi(rhs, lhs, &miss); 3490 3491 Register rhs_map = x10; 3492 Register lhs_map = x11; 3493 __ Ldr(rhs_map, FieldMemOperand(rhs, HeapObject::kMapOffset)); 3494 __ Ldr(lhs_map, FieldMemOperand(lhs, HeapObject::kMapOffset)); 3495 __ Cmp(rhs_map, Operand(known_map_)); 3496 __ B(ne, &miss); 3497 __ Cmp(lhs_map, Operand(known_map_)); 3498 __ B(ne, &miss); 3499 3500 __ Sub(result, rhs, lhs); 3501 __ Ret(); 3502 3503 __ Bind(&miss); 3504 GenerateMiss(masm); 3505 } 3506 3507 3508 // This method handles the case where a compare stub had the wrong 3509 // implementation. It calls a miss handler, which re-writes the stub. All other 3510 // CompareICStub::Generate* methods should fall back into this one if their 3511 // operands were not the expected types. 3512 void CompareICStub::GenerateMiss(MacroAssembler* masm) { 3513 ASM_LOCATION("CompareICStub[Miss]"); 3514 3515 Register stub_entry = x11; 3516 { 3517 ExternalReference miss = 3518 ExternalReference(IC_Utility(IC::kCompareIC_Miss), isolate()); 3519 3520 FrameScope scope(masm, StackFrame::INTERNAL); 3521 Register op = x10; 3522 Register left = x1; 3523 Register right = x0; 3524 // Preserve some caller-saved registers. 3525 __ Push(x1, x0, lr); 3526 // Push the arguments. 3527 __ Mov(op, Smi::FromInt(this->op())); 3528 __ Push(left, right, op); 3529 3530 // Call the miss handler. This also pops the arguments. 3531 __ CallExternalReference(miss, 3); 3532 3533 // Compute the entry point of the rewritten stub. 3534 __ Add(stub_entry, x0, Code::kHeaderSize - kHeapObjectTag); 3535 // Restore caller-saved registers. 3536 __ Pop(lr, x0, x1); 3537 } 3538 3539 // Tail-call to the new stub. 3540 __ Jump(stub_entry); 3541 } 3542 3543 3544 void SubStringStub::Generate(MacroAssembler* masm) { 3545 ASM_LOCATION("SubStringStub::Generate"); 3546 Label runtime; 3547 3548 // Stack frame on entry. 3549 // lr: return address 3550 // jssp[0]: substring "to" offset 3551 // jssp[8]: substring "from" offset 3552 // jssp[16]: pointer to string object 3553 3554 // This stub is called from the native-call %_SubString(...), so 3555 // nothing can be assumed about the arguments. It is tested that: 3556 // "string" is a sequential string, 3557 // both "from" and "to" are smis, and 3558 // 0 <= from <= to <= string.length (in debug mode.) 3559 // If any of these assumptions fail, we call the runtime system. 3560 3561 static const int kToOffset = 0 * kPointerSize; 3562 static const int kFromOffset = 1 * kPointerSize; 3563 static const int kStringOffset = 2 * kPointerSize; 3564 3565 Register to = x0; 3566 Register from = x15; 3567 Register input_string = x10; 3568 Register input_length = x11; 3569 Register input_type = x12; 3570 Register result_string = x0; 3571 Register result_length = x1; 3572 Register temp = x3; 3573 3574 __ Peek(to, kToOffset); 3575 __ Peek(from, kFromOffset); 3576 3577 // Check that both from and to are smis. If not, jump to runtime. 3578 __ JumpIfEitherNotSmi(from, to, &runtime); 3579 __ SmiUntag(from); 3580 __ SmiUntag(to); 3581 3582 // Calculate difference between from and to. If to < from, branch to runtime. 3583 __ Subs(result_length, to, from); 3584 __ B(mi, &runtime); 3585 3586 // Check from is positive. 3587 __ Tbnz(from, kWSignBit, &runtime); 3588 3589 // Make sure first argument is a string. 3590 __ Peek(input_string, kStringOffset); 3591 __ JumpIfSmi(input_string, &runtime); 3592 __ IsObjectJSStringType(input_string, input_type, &runtime); 3593 3594 Label single_char; 3595 __ Cmp(result_length, 1); 3596 __ B(eq, &single_char); 3597 3598 // Short-cut for the case of trivial substring. 3599 Label return_x0; 3600 __ Ldrsw(input_length, 3601 UntagSmiFieldMemOperand(input_string, String::kLengthOffset)); 3602 3603 __ Cmp(result_length, input_length); 3604 __ CmovX(x0, input_string, eq); 3605 // Return original string. 3606 __ B(eq, &return_x0); 3607 3608 // Longer than original string's length or negative: unsafe arguments. 3609 __ B(hi, &runtime); 3610 3611 // Shorter than original string's length: an actual substring. 3612 3613 // x0 to substring end character offset 3614 // x1 result_length length of substring result 3615 // x10 input_string pointer to input string object 3616 // x10 unpacked_string pointer to unpacked string object 3617 // x11 input_length length of input string 3618 // x12 input_type instance type of input string 3619 // x15 from substring start character offset 3620 3621 // Deal with different string types: update the index if necessary and put 3622 // the underlying string into register unpacked_string. 3623 Label underlying_unpacked, sliced_string, seq_or_external_string; 3624 Label update_instance_type; 3625 // If the string is not indirect, it can only be sequential or external. 3626 STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); 3627 STATIC_ASSERT(kIsIndirectStringMask != 0); 3628 3629 // Test for string types, and branch/fall through to appropriate unpacking 3630 // code. 3631 __ Tst(input_type, kIsIndirectStringMask); 3632 __ B(eq, &seq_or_external_string); 3633 __ Tst(input_type, kSlicedNotConsMask); 3634 __ B(ne, &sliced_string); 3635 3636 Register unpacked_string = input_string; 3637 3638 // Cons string. Check whether it is flat, then fetch first part. 3639 __ Ldr(temp, FieldMemOperand(input_string, ConsString::kSecondOffset)); 3640 __ JumpIfNotRoot(temp, Heap::kempty_stringRootIndex, &runtime); 3641 __ Ldr(unpacked_string, 3642 FieldMemOperand(input_string, ConsString::kFirstOffset)); 3643 __ B(&update_instance_type); 3644 3645 __ Bind(&sliced_string); 3646 // Sliced string. Fetch parent and correct start index by offset. 3647 __ Ldrsw(temp, 3648 UntagSmiFieldMemOperand(input_string, SlicedString::kOffsetOffset)); 3649 __ Add(from, from, temp); 3650 __ Ldr(unpacked_string, 3651 FieldMemOperand(input_string, SlicedString::kParentOffset)); 3652 3653 __ Bind(&update_instance_type); 3654 __ Ldr(temp, FieldMemOperand(unpacked_string, HeapObject::kMapOffset)); 3655 __ Ldrb(input_type, FieldMemOperand(temp, Map::kInstanceTypeOffset)); 3656 // Now control must go to &underlying_unpacked. Since the no code is generated 3657 // before then we fall through instead of generating a useless branch. 3658 3659 __ Bind(&seq_or_external_string); 3660 // Sequential or external string. Registers unpacked_string and input_string 3661 // alias, so there's nothing to do here. 3662 // Note that if code is added here, the above code must be updated. 3663 3664 // x0 result_string pointer to result string object (uninit) 3665 // x1 result_length length of substring result 3666 // x10 unpacked_string pointer to unpacked string object 3667 // x11 input_length length of input string 3668 // x12 input_type instance type of input string 3669 // x15 from substring start character offset 3670 __ Bind(&underlying_unpacked); 3671 3672 if (FLAG_string_slices) { 3673 Label copy_routine; 3674 __ Cmp(result_length, SlicedString::kMinLength); 3675 // Short slice. Copy instead of slicing. 3676 __ B(lt, ©_routine); 3677 // Allocate new sliced string. At this point we do not reload the instance 3678 // type including the string encoding because we simply rely on the info 3679 // provided by the original string. It does not matter if the original 3680 // string's encoding is wrong because we always have to recheck encoding of 3681 // the newly created string's parent anyway due to externalized strings. 3682 Label two_byte_slice, set_slice_header; 3683 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); 3684 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); 3685 __ Tbz(input_type, MaskToBit(kStringEncodingMask), &two_byte_slice); 3686 __ AllocateOneByteSlicedString(result_string, result_length, x3, x4, 3687 &runtime); 3688 __ B(&set_slice_header); 3689 3690 __ Bind(&two_byte_slice); 3691 __ AllocateTwoByteSlicedString(result_string, result_length, x3, x4, 3692 &runtime); 3693 3694 __ Bind(&set_slice_header); 3695 __ SmiTag(from); 3696 __ Str(from, FieldMemOperand(result_string, SlicedString::kOffsetOffset)); 3697 __ Str(unpacked_string, 3698 FieldMemOperand(result_string, SlicedString::kParentOffset)); 3699 __ B(&return_x0); 3700 3701 __ Bind(©_routine); 3702 } 3703 3704 // x0 result_string pointer to result string object (uninit) 3705 // x1 result_length length of substring result 3706 // x10 unpacked_string pointer to unpacked string object 3707 // x11 input_length length of input string 3708 // x12 input_type instance type of input string 3709 // x13 unpacked_char0 pointer to first char of unpacked string (uninit) 3710 // x13 substring_char0 pointer to first char of substring (uninit) 3711 // x14 result_char0 pointer to first char of result (uninit) 3712 // x15 from substring start character offset 3713 Register unpacked_char0 = x13; 3714 Register substring_char0 = x13; 3715 Register result_char0 = x14; 3716 Label two_byte_sequential, sequential_string, allocate_result; 3717 STATIC_ASSERT(kExternalStringTag != 0); 3718 STATIC_ASSERT(kSeqStringTag == 0); 3719 3720 __ Tst(input_type, kExternalStringTag); 3721 __ B(eq, &sequential_string); 3722 3723 __ Tst(input_type, kShortExternalStringTag); 3724 __ B(ne, &runtime); 3725 __ Ldr(unpacked_char0, 3726 FieldMemOperand(unpacked_string, ExternalString::kResourceDataOffset)); 3727 // unpacked_char0 points to the first character of the underlying string. 3728 __ B(&allocate_result); 3729 3730 __ Bind(&sequential_string); 3731 // Locate first character of underlying subject string. 3732 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); 3733 __ Add(unpacked_char0, unpacked_string, 3734 SeqOneByteString::kHeaderSize - kHeapObjectTag); 3735 3736 __ Bind(&allocate_result); 3737 // Sequential one-byte string. Allocate the result. 3738 STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0); 3739 __ Tbz(input_type, MaskToBit(kStringEncodingMask), &two_byte_sequential); 3740 3741 // Allocate and copy the resulting one-byte string. 3742 __ AllocateOneByteString(result_string, result_length, x3, x4, x5, &runtime); 3743 3744 // Locate first character of substring to copy. 3745 __ Add(substring_char0, unpacked_char0, from); 3746 3747 // Locate first character of result. 3748 __ Add(result_char0, result_string, 3749 SeqOneByteString::kHeaderSize - kHeapObjectTag); 3750 3751 STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0); 3752 __ CopyBytes(result_char0, substring_char0, result_length, x3, kCopyLong); 3753 __ B(&return_x0); 3754 3755 // Allocate and copy the resulting two-byte string. 3756 __ Bind(&two_byte_sequential); 3757 __ AllocateTwoByteString(result_string, result_length, x3, x4, x5, &runtime); 3758 3759 // Locate first character of substring to copy. 3760 __ Add(substring_char0, unpacked_char0, Operand(from, LSL, 1)); 3761 3762 // Locate first character of result. 3763 __ Add(result_char0, result_string, 3764 SeqTwoByteString::kHeaderSize - kHeapObjectTag); 3765 3766 STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0); 3767 __ Add(result_length, result_length, result_length); 3768 __ CopyBytes(result_char0, substring_char0, result_length, x3, kCopyLong); 3769 3770 __ Bind(&return_x0); 3771 Counters* counters = isolate()->counters(); 3772 __ IncrementCounter(counters->sub_string_native(), 1, x3, x4); 3773 __ Drop(3); 3774 __ Ret(); 3775 3776 __ Bind(&runtime); 3777 __ TailCallRuntime(Runtime::kSubString, 3, 1); 3778 3779 __ bind(&single_char); 3780 // x1: result_length 3781 // x10: input_string 3782 // x12: input_type 3783 // x15: from (untagged) 3784 __ SmiTag(from); 3785 StringCharAtGenerator generator( 3786 input_string, from, result_length, x0, 3787 &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER); 3788 generator.GenerateFast(masm); 3789 __ Drop(3); 3790 __ Ret(); 3791 generator.SkipSlow(masm, &runtime); 3792 } 3793 3794 3795 void StringHelper::GenerateFlatOneByteStringEquals( 3796 MacroAssembler* masm, Register left, Register right, Register scratch1, 3797 Register scratch2, Register scratch3) { 3798 DCHECK(!AreAliased(left, right, scratch1, scratch2, scratch3)); 3799 Register result = x0; 3800 Register left_length = scratch1; 3801 Register right_length = scratch2; 3802 3803 // Compare lengths. If lengths differ, strings can't be equal. Lengths are 3804 // smis, and don't need to be untagged. 3805 Label strings_not_equal, check_zero_length; 3806 __ Ldr(left_length, FieldMemOperand(left, String::kLengthOffset)); 3807 __ Ldr(right_length, FieldMemOperand(right, String::kLengthOffset)); 3808 __ Cmp(left_length, right_length); 3809 __ B(eq, &check_zero_length); 3810 3811 __ Bind(&strings_not_equal); 3812 __ Mov(result, Smi::FromInt(NOT_EQUAL)); 3813 __ Ret(); 3814 3815 // Check if the length is zero. If so, the strings must be equal (and empty.) 3816 Label compare_chars; 3817 __ Bind(&check_zero_length); 3818 STATIC_ASSERT(kSmiTag == 0); 3819 __ Cbnz(left_length, &compare_chars); 3820 __ Mov(result, Smi::FromInt(EQUAL)); 3821 __ Ret(); 3822 3823 // Compare characters. Falls through if all characters are equal. 3824 __ Bind(&compare_chars); 3825 GenerateOneByteCharsCompareLoop(masm, left, right, left_length, scratch2, 3826 scratch3, &strings_not_equal); 3827 3828 // Characters in strings are equal. 3829 __ Mov(result, Smi::FromInt(EQUAL)); 3830 __ Ret(); 3831 } 3832 3833 3834 void StringHelper::GenerateCompareFlatOneByteStrings( 3835 MacroAssembler* masm, Register left, Register right, Register scratch1, 3836 Register scratch2, Register scratch3, Register scratch4) { 3837 DCHECK(!AreAliased(left, right, scratch1, scratch2, scratch3, scratch4)); 3838 Label result_not_equal, compare_lengths; 3839 3840 // Find minimum length and length difference. 3841 Register length_delta = scratch3; 3842 __ Ldr(scratch1, FieldMemOperand(left, String::kLengthOffset)); 3843 __ Ldr(scratch2, FieldMemOperand(right, String::kLengthOffset)); 3844 __ Subs(length_delta, scratch1, scratch2); 3845 3846 Register min_length = scratch1; 3847 __ Csel(min_length, scratch2, scratch1, gt); 3848 __ Cbz(min_length, &compare_lengths); 3849 3850 // Compare loop. 3851 GenerateOneByteCharsCompareLoop(masm, left, right, min_length, scratch2, 3852 scratch4, &result_not_equal); 3853 3854 // Compare lengths - strings up to min-length are equal. 3855 __ Bind(&compare_lengths); 3856 3857 DCHECK(Smi::FromInt(EQUAL) == static_cast<Smi*>(0)); 3858 3859 // Use length_delta as result if it's zero. 3860 Register result = x0; 3861 __ Subs(result, length_delta, 0); 3862 3863 __ Bind(&result_not_equal); 3864 Register greater = x10; 3865 Register less = x11; 3866 __ Mov(greater, Smi::FromInt(GREATER)); 3867 __ Mov(less, Smi::FromInt(LESS)); 3868 __ CmovX(result, greater, gt); 3869 __ CmovX(result, less, lt); 3870 __ Ret(); 3871 } 3872 3873 3874 void StringHelper::GenerateOneByteCharsCompareLoop( 3875 MacroAssembler* masm, Register left, Register right, Register length, 3876 Register scratch1, Register scratch2, Label* chars_not_equal) { 3877 DCHECK(!AreAliased(left, right, length, scratch1, scratch2)); 3878 3879 // Change index to run from -length to -1 by adding length to string 3880 // start. This means that loop ends when index reaches zero, which 3881 // doesn't need an additional compare. 3882 __ SmiUntag(length); 3883 __ Add(scratch1, length, SeqOneByteString::kHeaderSize - kHeapObjectTag); 3884 __ Add(left, left, scratch1); 3885 __ Add(right, right, scratch1); 3886 3887 Register index = length; 3888 __ Neg(index, length); // index = -length; 3889 3890 // Compare loop 3891 Label loop; 3892 __ Bind(&loop); 3893 __ Ldrb(scratch1, MemOperand(left, index)); 3894 __ Ldrb(scratch2, MemOperand(right, index)); 3895 __ Cmp(scratch1, scratch2); 3896 __ B(ne, chars_not_equal); 3897 __ Add(index, index, 1); 3898 __ Cbnz(index, &loop); 3899 } 3900 3901 3902 void StringCompareStub::Generate(MacroAssembler* masm) { 3903 Label runtime; 3904 3905 Counters* counters = isolate()->counters(); 3906 3907 // Stack frame on entry. 3908 // sp[0]: right string 3909 // sp[8]: left string 3910 Register right = x10; 3911 Register left = x11; 3912 Register result = x0; 3913 __ Pop(right, left); 3914 3915 Label not_same; 3916 __ Subs(result, right, left); 3917 __ B(ne, ¬_same); 3918 STATIC_ASSERT(EQUAL == 0); 3919 __ IncrementCounter(counters->string_compare_native(), 1, x3, x4); 3920 __ Ret(); 3921 3922 __ Bind(¬_same); 3923 3924 // Check that both objects are sequential one-byte strings. 3925 __ JumpIfEitherIsNotSequentialOneByteStrings(left, right, x12, x13, &runtime); 3926 3927 // Compare flat one-byte strings natively. Remove arguments from stack first, 3928 // as this function will generate a return. 3929 __ IncrementCounter(counters->string_compare_native(), 1, x3, x4); 3930 StringHelper::GenerateCompareFlatOneByteStrings(masm, left, right, x12, x13, 3931 x14, x15); 3932 3933 __ Bind(&runtime); 3934 3935 // Push arguments back on to the stack. 3936 // sp[0] = right string 3937 // sp[8] = left string. 3938 __ Push(left, right); 3939 3940 // Call the runtime. 3941 // Returns -1 (less), 0 (equal), or 1 (greater) tagged as a small integer. 3942 __ TailCallRuntime(Runtime::kStringCompare, 2, 1); 3943 } 3944 3945 3946 void BinaryOpICWithAllocationSiteStub::Generate(MacroAssembler* masm) { 3947 // ----------- S t a t e ------------- 3948 // -- x1 : left 3949 // -- x0 : right 3950 // -- lr : return address 3951 // ----------------------------------- 3952 3953 // Load x2 with the allocation site. We stick an undefined dummy value here 3954 // and replace it with the real allocation site later when we instantiate this 3955 // stub in BinaryOpICWithAllocationSiteStub::GetCodeCopyFromTemplate(). 3956 __ LoadObject(x2, handle(isolate()->heap()->undefined_value())); 3957 3958 // Make sure that we actually patched the allocation site. 3959 if (FLAG_debug_code) { 3960 __ AssertNotSmi(x2, kExpectedAllocationSite); 3961 __ Ldr(x10, FieldMemOperand(x2, HeapObject::kMapOffset)); 3962 __ AssertRegisterIsRoot(x10, Heap::kAllocationSiteMapRootIndex, 3963 kExpectedAllocationSite); 3964 } 3965 3966 // Tail call into the stub that handles binary operations with allocation 3967 // sites. 3968 BinaryOpWithAllocationSiteStub stub(isolate(), state()); 3969 __ TailCallStub(&stub); 3970 } 3971 3972 3973 void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { 3974 // We need some extra registers for this stub, they have been allocated 3975 // but we need to save them before using them. 3976 regs_.Save(masm); 3977 3978 if (remembered_set_action() == EMIT_REMEMBERED_SET) { 3979 Label dont_need_remembered_set; 3980 3981 Register val = regs_.scratch0(); 3982 __ Ldr(val, MemOperand(regs_.address())); 3983 __ JumpIfNotInNewSpace(val, &dont_need_remembered_set); 3984 3985 __ CheckPageFlagSet(regs_.object(), val, 1 << MemoryChunk::SCAN_ON_SCAVENGE, 3986 &dont_need_remembered_set); 3987 3988 // First notify the incremental marker if necessary, then update the 3989 // remembered set. 3990 CheckNeedsToInformIncrementalMarker( 3991 masm, kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, mode); 3992 InformIncrementalMarker(masm); 3993 regs_.Restore(masm); // Restore the extra scratch registers we used. 3994 3995 __ RememberedSetHelper(object(), address(), 3996 value(), // scratch1 3997 save_fp_regs_mode(), MacroAssembler::kReturnAtEnd); 3998 3999 __ Bind(&dont_need_remembered_set); 4000 } 4001 4002 CheckNeedsToInformIncrementalMarker( 4003 masm, kReturnOnNoNeedToInformIncrementalMarker, mode); 4004 InformIncrementalMarker(masm); 4005 regs_.Restore(masm); // Restore the extra scratch registers we used. 4006 __ Ret(); 4007 } 4008 4009 4010 void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm) { 4011 regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode()); 4012 Register address = 4013 x0.Is(regs_.address()) ? regs_.scratch0() : regs_.address(); 4014 DCHECK(!address.Is(regs_.object())); 4015 DCHECK(!address.Is(x0)); 4016 __ Mov(address, regs_.address()); 4017 __ Mov(x0, regs_.object()); 4018 __ Mov(x1, address); 4019 __ Mov(x2, ExternalReference::isolate_address(isolate())); 4020 4021 AllowExternalCallThatCantCauseGC scope(masm); 4022 ExternalReference function = 4023 ExternalReference::incremental_marking_record_write_function( 4024 isolate()); 4025 __ CallCFunction(function, 3, 0); 4026 4027 regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode()); 4028 } 4029 4030 4031 void RecordWriteStub::CheckNeedsToInformIncrementalMarker( 4032 MacroAssembler* masm, 4033 OnNoNeedToInformIncrementalMarker on_no_need, 4034 Mode mode) { 4035 Label on_black; 4036 Label need_incremental; 4037 Label need_incremental_pop_scratch; 4038 4039 Register mem_chunk = regs_.scratch0(); 4040 Register counter = regs_.scratch1(); 4041 __ Bic(mem_chunk, regs_.object(), Page::kPageAlignmentMask); 4042 __ Ldr(counter, 4043 MemOperand(mem_chunk, MemoryChunk::kWriteBarrierCounterOffset)); 4044 __ Subs(counter, counter, 1); 4045 __ Str(counter, 4046 MemOperand(mem_chunk, MemoryChunk::kWriteBarrierCounterOffset)); 4047 __ B(mi, &need_incremental); 4048 4049 // If the object is not black we don't have to inform the incremental marker. 4050 __ JumpIfBlack(regs_.object(), regs_.scratch0(), regs_.scratch1(), &on_black); 4051 4052 regs_.Restore(masm); // Restore the extra scratch registers we used. 4053 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { 4054 __ RememberedSetHelper(object(), address(), 4055 value(), // scratch1 4056 save_fp_regs_mode(), MacroAssembler::kReturnAtEnd); 4057 } else { 4058 __ Ret(); 4059 } 4060 4061 __ Bind(&on_black); 4062 // Get the value from the slot. 4063 Register val = regs_.scratch0(); 4064 __ Ldr(val, MemOperand(regs_.address())); 4065 4066 if (mode == INCREMENTAL_COMPACTION) { 4067 Label ensure_not_white; 4068 4069 __ CheckPageFlagClear(val, regs_.scratch1(), 4070 MemoryChunk::kEvacuationCandidateMask, 4071 &ensure_not_white); 4072 4073 __ CheckPageFlagClear(regs_.object(), 4074 regs_.scratch1(), 4075 MemoryChunk::kSkipEvacuationSlotsRecordingMask, 4076 &need_incremental); 4077 4078 __ Bind(&ensure_not_white); 4079 } 4080 4081 // We need extra registers for this, so we push the object and the address 4082 // register temporarily. 4083 __ Push(regs_.address(), regs_.object()); 4084 __ EnsureNotWhite(val, 4085 regs_.scratch1(), // Scratch. 4086 regs_.object(), // Scratch. 4087 regs_.address(), // Scratch. 4088 regs_.scratch2(), // Scratch. 4089 &need_incremental_pop_scratch); 4090 __ Pop(regs_.object(), regs_.address()); 4091 4092 regs_.Restore(masm); // Restore the extra scratch registers we used. 4093 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { 4094 __ RememberedSetHelper(object(), address(), 4095 value(), // scratch1 4096 save_fp_regs_mode(), MacroAssembler::kReturnAtEnd); 4097 } else { 4098 __ Ret(); 4099 } 4100 4101 __ Bind(&need_incremental_pop_scratch); 4102 __ Pop(regs_.object(), regs_.address()); 4103 4104 __ Bind(&need_incremental); 4105 // Fall through when we need to inform the incremental marker. 4106 } 4107 4108 4109 void RecordWriteStub::Generate(MacroAssembler* masm) { 4110 Label skip_to_incremental_noncompacting; 4111 Label skip_to_incremental_compacting; 4112 4113 // We patch these two first instructions back and forth between a nop and 4114 // real branch when we start and stop incremental heap marking. 4115 // Initially the stub is expected to be in STORE_BUFFER_ONLY mode, so 2 nops 4116 // are generated. 4117 // See RecordWriteStub::Patch for details. 4118 { 4119 InstructionAccurateScope scope(masm, 2); 4120 __ adr(xzr, &skip_to_incremental_noncompacting); 4121 __ adr(xzr, &skip_to_incremental_compacting); 4122 } 4123 4124 if (remembered_set_action() == EMIT_REMEMBERED_SET) { 4125 __ RememberedSetHelper(object(), address(), 4126 value(), // scratch1 4127 save_fp_regs_mode(), MacroAssembler::kReturnAtEnd); 4128 } 4129 __ Ret(); 4130 4131 __ Bind(&skip_to_incremental_noncompacting); 4132 GenerateIncremental(masm, INCREMENTAL); 4133 4134 __ Bind(&skip_to_incremental_compacting); 4135 GenerateIncremental(masm, INCREMENTAL_COMPACTION); 4136 } 4137 4138 4139 void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { 4140 // x0 value element value to store 4141 // x3 index_smi element index as smi 4142 // sp[0] array_index_smi array literal index in function as smi 4143 // sp[1] array array literal 4144 4145 Register value = x0; 4146 Register index_smi = x3; 4147 4148 Register array = x1; 4149 Register array_map = x2; 4150 Register array_index_smi = x4; 4151 __ PeekPair(array_index_smi, array, 0); 4152 __ Ldr(array_map, FieldMemOperand(array, JSObject::kMapOffset)); 4153 4154 Label double_elements, smi_element, fast_elements, slow_elements; 4155 Register bitfield2 = x10; 4156 __ Ldrb(bitfield2, FieldMemOperand(array_map, Map::kBitField2Offset)); 4157 4158 // Jump if array's ElementsKind is not FAST*_SMI_ELEMENTS, FAST_ELEMENTS or 4159 // FAST_HOLEY_ELEMENTS. 4160 STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); 4161 STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); 4162 STATIC_ASSERT(FAST_ELEMENTS == 2); 4163 STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); 4164 __ Cmp(bitfield2, Map::kMaximumBitField2FastHoleyElementValue); 4165 __ B(hi, &double_elements); 4166 4167 __ JumpIfSmi(value, &smi_element); 4168 4169 // Jump if array's ElementsKind is not FAST_ELEMENTS or FAST_HOLEY_ELEMENTS. 4170 __ Tbnz(bitfield2, MaskToBit(FAST_ELEMENTS << Map::ElementsKindBits::kShift), 4171 &fast_elements); 4172 4173 // Store into the array literal requires an elements transition. Call into 4174 // the runtime. 4175 __ Bind(&slow_elements); 4176 __ Push(array, index_smi, value); 4177 __ Ldr(x10, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 4178 __ Ldr(x11, FieldMemOperand(x10, JSFunction::kLiteralsOffset)); 4179 __ Push(x11, array_index_smi); 4180 __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); 4181 4182 // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object. 4183 __ Bind(&fast_elements); 4184 __ Ldr(x10, FieldMemOperand(array, JSObject::kElementsOffset)); 4185 __ Add(x11, x10, Operand::UntagSmiAndScale(index_smi, kPointerSizeLog2)); 4186 __ Add(x11, x11, FixedArray::kHeaderSize - kHeapObjectTag); 4187 __ Str(value, MemOperand(x11)); 4188 // Update the write barrier for the array store. 4189 __ RecordWrite(x10, x11, value, kLRHasNotBeenSaved, kDontSaveFPRegs, 4190 EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); 4191 __ Ret(); 4192 4193 // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS, 4194 // and value is Smi. 4195 __ Bind(&smi_element); 4196 __ Ldr(x10, FieldMemOperand(array, JSObject::kElementsOffset)); 4197 __ Add(x11, x10, Operand::UntagSmiAndScale(index_smi, kPointerSizeLog2)); 4198 __ Str(value, FieldMemOperand(x11, FixedArray::kHeaderSize)); 4199 __ Ret(); 4200 4201 __ Bind(&double_elements); 4202 __ Ldr(x10, FieldMemOperand(array, JSObject::kElementsOffset)); 4203 __ StoreNumberToDoubleElements(value, index_smi, x10, x11, d0, 4204 &slow_elements); 4205 __ Ret(); 4206 } 4207 4208 4209 void StubFailureTrampolineStub::Generate(MacroAssembler* masm) { 4210 CEntryStub ces(isolate(), 1, kSaveFPRegs); 4211 __ Call(ces.GetCode(), RelocInfo::CODE_TARGET); 4212 int parameter_count_offset = 4213 StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset; 4214 __ Ldr(x1, MemOperand(fp, parameter_count_offset)); 4215 if (function_mode() == JS_FUNCTION_STUB_MODE) { 4216 __ Add(x1, x1, 1); 4217 } 4218 masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE); 4219 __ Drop(x1); 4220 // Return to IC Miss stub, continuation still on stack. 4221 __ Ret(); 4222 } 4223 4224 4225 void LoadICTrampolineStub::Generate(MacroAssembler* masm) { 4226 EmitLoadTypeFeedbackVector(masm, VectorLoadICDescriptor::VectorRegister()); 4227 VectorLoadStub stub(isolate(), state()); 4228 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); 4229 } 4230 4231 4232 void KeyedLoadICTrampolineStub::Generate(MacroAssembler* masm) { 4233 EmitLoadTypeFeedbackVector(masm, VectorLoadICDescriptor::VectorRegister()); 4234 VectorKeyedLoadStub stub(isolate()); 4235 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); 4236 } 4237 4238 4239 static unsigned int GetProfileEntryHookCallSize(MacroAssembler* masm) { 4240 // The entry hook is a "BumpSystemStackPointer" instruction (sub), 4241 // followed by a "Push lr" instruction, followed by a call. 4242 unsigned int size = 4243 Assembler::kCallSizeWithRelocation + (2 * kInstructionSize); 4244 if (CpuFeatures::IsSupported(ALWAYS_ALIGN_CSP)) { 4245 // If ALWAYS_ALIGN_CSP then there will be an extra bic instruction in 4246 // "BumpSystemStackPointer". 4247 size += kInstructionSize; 4248 } 4249 return size; 4250 } 4251 4252 4253 void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { 4254 if (masm->isolate()->function_entry_hook() != NULL) { 4255 ProfileEntryHookStub stub(masm->isolate()); 4256 Assembler::BlockConstPoolScope no_const_pools(masm); 4257 DontEmitDebugCodeScope no_debug_code(masm); 4258 Label entry_hook_call_start; 4259 __ Bind(&entry_hook_call_start); 4260 __ Push(lr); 4261 __ CallStub(&stub); 4262 DCHECK(masm->SizeOfCodeGeneratedSince(&entry_hook_call_start) == 4263 GetProfileEntryHookCallSize(masm)); 4264 4265 __ Pop(lr); 4266 } 4267 } 4268 4269 4270 void ProfileEntryHookStub::Generate(MacroAssembler* masm) { 4271 MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm); 4272 4273 // Save all kCallerSaved registers (including lr), since this can be called 4274 // from anywhere. 4275 // TODO(jbramley): What about FP registers? 4276 __ PushCPURegList(kCallerSaved); 4277 DCHECK(kCallerSaved.IncludesAliasOf(lr)); 4278 const int kNumSavedRegs = kCallerSaved.Count(); 4279 4280 // Compute the function's address as the first argument. 4281 __ Sub(x0, lr, GetProfileEntryHookCallSize(masm)); 4282 4283 #if V8_HOST_ARCH_ARM64 4284 uintptr_t entry_hook = 4285 reinterpret_cast<uintptr_t>(isolate()->function_entry_hook()); 4286 __ Mov(x10, entry_hook); 4287 #else 4288 // Under the simulator we need to indirect the entry hook through a trampoline 4289 // function at a known address. 4290 ApiFunction dispatcher(FUNCTION_ADDR(EntryHookTrampoline)); 4291 __ Mov(x10, Operand(ExternalReference(&dispatcher, 4292 ExternalReference::BUILTIN_CALL, 4293 isolate()))); 4294 // It additionally takes an isolate as a third parameter 4295 __ Mov(x2, ExternalReference::isolate_address(isolate())); 4296 #endif 4297 4298 // The caller's return address is above the saved temporaries. 4299 // Grab its location for the second argument to the hook. 4300 __ Add(x1, __ StackPointer(), kNumSavedRegs * kPointerSize); 4301 4302 { 4303 // Create a dummy frame, as CallCFunction requires this. 4304 FrameScope frame(masm, StackFrame::MANUAL); 4305 __ CallCFunction(x10, 2, 0); 4306 } 4307 4308 __ PopCPURegList(kCallerSaved); 4309 __ Ret(); 4310 } 4311 4312 4313 void DirectCEntryStub::Generate(MacroAssembler* masm) { 4314 // When calling into C++ code the stack pointer must be csp. 4315 // Therefore this code must use csp for peek/poke operations when the 4316 // stub is generated. When the stub is called 4317 // (via DirectCEntryStub::GenerateCall), the caller must setup an ExitFrame 4318 // and configure the stack pointer *before* doing the call. 4319 const Register old_stack_pointer = __ StackPointer(); 4320 __ SetStackPointer(csp); 4321 4322 // Put return address on the stack (accessible to GC through exit frame pc). 4323 __ Poke(lr, 0); 4324 // Call the C++ function. 4325 __ Blr(x10); 4326 // Return to calling code. 4327 __ Peek(lr, 0); 4328 __ AssertFPCRState(); 4329 __ Ret(); 4330 4331 __ SetStackPointer(old_stack_pointer); 4332 } 4333 4334 void DirectCEntryStub::GenerateCall(MacroAssembler* masm, 4335 Register target) { 4336 // Make sure the caller configured the stack pointer (see comment in 4337 // DirectCEntryStub::Generate). 4338 DCHECK(csp.Is(__ StackPointer())); 4339 4340 intptr_t code = 4341 reinterpret_cast<intptr_t>(GetCode().location()); 4342 __ Mov(lr, Operand(code, RelocInfo::CODE_TARGET)); 4343 __ Mov(x10, target); 4344 // Branch to the stub. 4345 __ Blr(lr); 4346 } 4347 4348 4349 // Probe the name dictionary in the 'elements' register. 4350 // Jump to the 'done' label if a property with the given name is found. 4351 // Jump to the 'miss' label otherwise. 4352 // 4353 // If lookup was successful 'scratch2' will be equal to elements + 4 * index. 4354 // 'elements' and 'name' registers are preserved on miss. 4355 void NameDictionaryLookupStub::GeneratePositiveLookup( 4356 MacroAssembler* masm, 4357 Label* miss, 4358 Label* done, 4359 Register elements, 4360 Register name, 4361 Register scratch1, 4362 Register scratch2) { 4363 DCHECK(!AreAliased(elements, name, scratch1, scratch2)); 4364 4365 // Assert that name contains a string. 4366 __ AssertName(name); 4367 4368 // Compute the capacity mask. 4369 __ Ldrsw(scratch1, UntagSmiFieldMemOperand(elements, kCapacityOffset)); 4370 __ Sub(scratch1, scratch1, 1); 4371 4372 // Generate an unrolled loop that performs a few probes before giving up. 4373 for (int i = 0; i < kInlinedProbes; i++) { 4374 // Compute the masked index: (hash + i + i * i) & mask. 4375 __ Ldr(scratch2, FieldMemOperand(name, Name::kHashFieldOffset)); 4376 if (i > 0) { 4377 // Add the probe offset (i + i * i) left shifted to avoid right shifting 4378 // the hash in a separate instruction. The value hash + i + i * i is right 4379 // shifted in the following and instruction. 4380 DCHECK(NameDictionary::GetProbeOffset(i) < 4381 1 << (32 - Name::kHashFieldOffset)); 4382 __ Add(scratch2, scratch2, Operand( 4383 NameDictionary::GetProbeOffset(i) << Name::kHashShift)); 4384 } 4385 __ And(scratch2, scratch1, Operand(scratch2, LSR, Name::kHashShift)); 4386 4387 // Scale the index by multiplying by the element size. 4388 DCHECK(NameDictionary::kEntrySize == 3); 4389 __ Add(scratch2, scratch2, Operand(scratch2, LSL, 1)); 4390 4391 // Check if the key is identical to the name. 4392 UseScratchRegisterScope temps(masm); 4393 Register scratch3 = temps.AcquireX(); 4394 __ Add(scratch2, elements, Operand(scratch2, LSL, kPointerSizeLog2)); 4395 __ Ldr(scratch3, FieldMemOperand(scratch2, kElementsStartOffset)); 4396 __ Cmp(name, scratch3); 4397 __ B(eq, done); 4398 } 4399 4400 // The inlined probes didn't find the entry. 4401 // Call the complete stub to scan the whole dictionary. 4402 4403 CPURegList spill_list(CPURegister::kRegister, kXRegSizeInBits, 0, 6); 4404 spill_list.Combine(lr); 4405 spill_list.Remove(scratch1); 4406 spill_list.Remove(scratch2); 4407 4408 __ PushCPURegList(spill_list); 4409 4410 if (name.is(x0)) { 4411 DCHECK(!elements.is(x1)); 4412 __ Mov(x1, name); 4413 __ Mov(x0, elements); 4414 } else { 4415 __ Mov(x0, elements); 4416 __ Mov(x1, name); 4417 } 4418 4419 Label not_found; 4420 NameDictionaryLookupStub stub(masm->isolate(), POSITIVE_LOOKUP); 4421 __ CallStub(&stub); 4422 __ Cbz(x0, ¬_found); 4423 __ Mov(scratch2, x2); // Move entry index into scratch2. 4424 __ PopCPURegList(spill_list); 4425 __ B(done); 4426 4427 __ Bind(¬_found); 4428 __ PopCPURegList(spill_list); 4429 __ B(miss); 4430 } 4431 4432 4433 void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, 4434 Label* miss, 4435 Label* done, 4436 Register receiver, 4437 Register properties, 4438 Handle<Name> name, 4439 Register scratch0) { 4440 DCHECK(!AreAliased(receiver, properties, scratch0)); 4441 DCHECK(name->IsUniqueName()); 4442 // If names of slots in range from 1 to kProbes - 1 for the hash value are 4443 // not equal to the name and kProbes-th slot is not used (its name is the 4444 // undefined value), it guarantees the hash table doesn't contain the 4445 // property. It's true even if some slots represent deleted properties 4446 // (their names are the hole value). 4447 for (int i = 0; i < kInlinedProbes; i++) { 4448 // scratch0 points to properties hash. 4449 // Compute the masked index: (hash + i + i * i) & mask. 4450 Register index = scratch0; 4451 // Capacity is smi 2^n. 4452 __ Ldrsw(index, UntagSmiFieldMemOperand(properties, kCapacityOffset)); 4453 __ Sub(index, index, 1); 4454 __ And(index, index, name->Hash() + NameDictionary::GetProbeOffset(i)); 4455 4456 // Scale the index by multiplying by the entry size. 4457 DCHECK(NameDictionary::kEntrySize == 3); 4458 __ Add(index, index, Operand(index, LSL, 1)); // index *= 3. 4459 4460 Register entity_name = scratch0; 4461 // Having undefined at this place means the name is not contained. 4462 Register tmp = index; 4463 __ Add(tmp, properties, Operand(index, LSL, kPointerSizeLog2)); 4464 __ Ldr(entity_name, FieldMemOperand(tmp, kElementsStartOffset)); 4465 4466 __ JumpIfRoot(entity_name, Heap::kUndefinedValueRootIndex, done); 4467 4468 // Stop if found the property. 4469 __ Cmp(entity_name, Operand(name)); 4470 __ B(eq, miss); 4471 4472 Label good; 4473 __ JumpIfRoot(entity_name, Heap::kTheHoleValueRootIndex, &good); 4474 4475 // Check if the entry name is not a unique name. 4476 __ Ldr(entity_name, FieldMemOperand(entity_name, HeapObject::kMapOffset)); 4477 __ Ldrb(entity_name, 4478 FieldMemOperand(entity_name, Map::kInstanceTypeOffset)); 4479 __ JumpIfNotUniqueNameInstanceType(entity_name, miss); 4480 __ Bind(&good); 4481 } 4482 4483 CPURegList spill_list(CPURegister::kRegister, kXRegSizeInBits, 0, 6); 4484 spill_list.Combine(lr); 4485 spill_list.Remove(scratch0); // Scratch registers don't need to be preserved. 4486 4487 __ PushCPURegList(spill_list); 4488 4489 __ Ldr(x0, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); 4490 __ Mov(x1, Operand(name)); 4491 NameDictionaryLookupStub stub(masm->isolate(), NEGATIVE_LOOKUP); 4492 __ CallStub(&stub); 4493 // Move stub return value to scratch0. Note that scratch0 is not included in 4494 // spill_list and won't be clobbered by PopCPURegList. 4495 __ Mov(scratch0, x0); 4496 __ PopCPURegList(spill_list); 4497 4498 __ Cbz(scratch0, done); 4499 __ B(miss); 4500 } 4501 4502 4503 void NameDictionaryLookupStub::Generate(MacroAssembler* masm) { 4504 // This stub overrides SometimesSetsUpAFrame() to return false. That means 4505 // we cannot call anything that could cause a GC from this stub. 4506 // 4507 // Arguments are in x0 and x1: 4508 // x0: property dictionary. 4509 // x1: the name of the property we are looking for. 4510 // 4511 // Return value is in x0 and is zero if lookup failed, non zero otherwise. 4512 // If the lookup is successful, x2 will contains the index of the entry. 4513 4514 Register result = x0; 4515 Register dictionary = x0; 4516 Register key = x1; 4517 Register index = x2; 4518 Register mask = x3; 4519 Register hash = x4; 4520 Register undefined = x5; 4521 Register entry_key = x6; 4522 4523 Label in_dictionary, maybe_in_dictionary, not_in_dictionary; 4524 4525 __ Ldrsw(mask, UntagSmiFieldMemOperand(dictionary, kCapacityOffset)); 4526 __ Sub(mask, mask, 1); 4527 4528 __ Ldr(hash, FieldMemOperand(key, Name::kHashFieldOffset)); 4529 __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex); 4530 4531 for (int i = kInlinedProbes; i < kTotalProbes; i++) { 4532 // Compute the masked index: (hash + i + i * i) & mask. 4533 // Capacity is smi 2^n. 4534 if (i > 0) { 4535 // Add the probe offset (i + i * i) left shifted to avoid right shifting 4536 // the hash in a separate instruction. The value hash + i + i * i is right 4537 // shifted in the following and instruction. 4538 DCHECK(NameDictionary::GetProbeOffset(i) < 4539 1 << (32 - Name::kHashFieldOffset)); 4540 __ Add(index, hash, 4541 NameDictionary::GetProbeOffset(i) << Name::kHashShift); 4542 } else { 4543 __ Mov(index, hash); 4544 } 4545 __ And(index, mask, Operand(index, LSR, Name::kHashShift)); 4546 4547 // Scale the index by multiplying by the entry size. 4548 DCHECK(NameDictionary::kEntrySize == 3); 4549 __ Add(index, index, Operand(index, LSL, 1)); // index *= 3. 4550 4551 __ Add(index, dictionary, Operand(index, LSL, kPointerSizeLog2)); 4552 __ Ldr(entry_key, FieldMemOperand(index, kElementsStartOffset)); 4553 4554 // Having undefined at this place means the name is not contained. 4555 __ Cmp(entry_key, undefined); 4556 __ B(eq, ¬_in_dictionary); 4557 4558 // Stop if found the property. 4559 __ Cmp(entry_key, key); 4560 __ B(eq, &in_dictionary); 4561 4562 if (i != kTotalProbes - 1 && mode() == NEGATIVE_LOOKUP) { 4563 // Check if the entry name is not a unique name. 4564 __ Ldr(entry_key, FieldMemOperand(entry_key, HeapObject::kMapOffset)); 4565 __ Ldrb(entry_key, FieldMemOperand(entry_key, Map::kInstanceTypeOffset)); 4566 __ JumpIfNotUniqueNameInstanceType(entry_key, &maybe_in_dictionary); 4567 } 4568 } 4569 4570 __ Bind(&maybe_in_dictionary); 4571 // If we are doing negative lookup then probing failure should be 4572 // treated as a lookup success. For positive lookup, probing failure 4573 // should be treated as lookup failure. 4574 if (mode() == POSITIVE_LOOKUP) { 4575 __ Mov(result, 0); 4576 __ Ret(); 4577 } 4578 4579 __ Bind(&in_dictionary); 4580 __ Mov(result, 1); 4581 __ Ret(); 4582 4583 __ Bind(¬_in_dictionary); 4584 __ Mov(result, 0); 4585 __ Ret(); 4586 } 4587 4588 4589 template<class T> 4590 static void CreateArrayDispatch(MacroAssembler* masm, 4591 AllocationSiteOverrideMode mode) { 4592 ASM_LOCATION("CreateArrayDispatch"); 4593 if (mode == DISABLE_ALLOCATION_SITES) { 4594 T stub(masm->isolate(), GetInitialFastElementsKind(), mode); 4595 __ TailCallStub(&stub); 4596 4597 } else if (mode == DONT_OVERRIDE) { 4598 Register kind = x3; 4599 int last_index = 4600 GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); 4601 for (int i = 0; i <= last_index; ++i) { 4602 Label next; 4603 ElementsKind candidate_kind = GetFastElementsKindFromSequenceIndex(i); 4604 // TODO(jbramley): Is this the best way to handle this? Can we make the 4605 // tail calls conditional, rather than hopping over each one? 4606 __ CompareAndBranch(kind, candidate_kind, ne, &next); 4607 T stub(masm->isolate(), candidate_kind); 4608 __ TailCallStub(&stub); 4609 __ Bind(&next); 4610 } 4611 4612 // If we reached this point there is a problem. 4613 __ Abort(kUnexpectedElementsKindInArrayConstructor); 4614 4615 } else { 4616 UNREACHABLE(); 4617 } 4618 } 4619 4620 4621 // TODO(jbramley): If this needs to be a special case, make it a proper template 4622 // specialization, and not a separate function. 4623 static void CreateArrayDispatchOneArgument(MacroAssembler* masm, 4624 AllocationSiteOverrideMode mode) { 4625 ASM_LOCATION("CreateArrayDispatchOneArgument"); 4626 // x0 - argc 4627 // x1 - constructor? 4628 // x2 - allocation site (if mode != DISABLE_ALLOCATION_SITES) 4629 // x3 - kind (if mode != DISABLE_ALLOCATION_SITES) 4630 // sp[0] - last argument 4631 4632 Register allocation_site = x2; 4633 Register kind = x3; 4634 4635 Label normal_sequence; 4636 if (mode == DONT_OVERRIDE) { 4637 STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); 4638 STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); 4639 STATIC_ASSERT(FAST_ELEMENTS == 2); 4640 STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); 4641 STATIC_ASSERT(FAST_DOUBLE_ELEMENTS == 4); 4642 STATIC_ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5); 4643 4644 // Is the low bit set? If so, the array is holey. 4645 __ Tbnz(kind, 0, &normal_sequence); 4646 } 4647 4648 // Look at the last argument. 4649 // TODO(jbramley): What does a 0 argument represent? 4650 __ Peek(x10, 0); 4651 __ Cbz(x10, &normal_sequence); 4652 4653 if (mode == DISABLE_ALLOCATION_SITES) { 4654 ElementsKind initial = GetInitialFastElementsKind(); 4655 ElementsKind holey_initial = GetHoleyElementsKind(initial); 4656 4657 ArraySingleArgumentConstructorStub stub_holey(masm->isolate(), 4658 holey_initial, 4659 DISABLE_ALLOCATION_SITES); 4660 __ TailCallStub(&stub_holey); 4661 4662 __ Bind(&normal_sequence); 4663 ArraySingleArgumentConstructorStub stub(masm->isolate(), 4664 initial, 4665 DISABLE_ALLOCATION_SITES); 4666 __ TailCallStub(&stub); 4667 } else if (mode == DONT_OVERRIDE) { 4668 // We are going to create a holey array, but our kind is non-holey. 4669 // Fix kind and retry (only if we have an allocation site in the slot). 4670 __ Orr(kind, kind, 1); 4671 4672 if (FLAG_debug_code) { 4673 __ Ldr(x10, FieldMemOperand(allocation_site, 0)); 4674 __ JumpIfNotRoot(x10, Heap::kAllocationSiteMapRootIndex, 4675 &normal_sequence); 4676 __ Assert(eq, kExpectedAllocationSite); 4677 } 4678 4679 // Save the resulting elements kind in type info. We can't just store 'kind' 4680 // in the AllocationSite::transition_info field because elements kind is 4681 // restricted to a portion of the field; upper bits need to be left alone. 4682 STATIC_ASSERT(AllocationSite::ElementsKindBits::kShift == 0); 4683 __ Ldr(x11, FieldMemOperand(allocation_site, 4684 AllocationSite::kTransitionInfoOffset)); 4685 __ Add(x11, x11, Smi::FromInt(kFastElementsKindPackedToHoley)); 4686 __ Str(x11, FieldMemOperand(allocation_site, 4687 AllocationSite::kTransitionInfoOffset)); 4688 4689 __ Bind(&normal_sequence); 4690 int last_index = 4691 GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); 4692 for (int i = 0; i <= last_index; ++i) { 4693 Label next; 4694 ElementsKind candidate_kind = GetFastElementsKindFromSequenceIndex(i); 4695 __ CompareAndBranch(kind, candidate_kind, ne, &next); 4696 ArraySingleArgumentConstructorStub stub(masm->isolate(), candidate_kind); 4697 __ TailCallStub(&stub); 4698 __ Bind(&next); 4699 } 4700 4701 // If we reached this point there is a problem. 4702 __ Abort(kUnexpectedElementsKindInArrayConstructor); 4703 } else { 4704 UNREACHABLE(); 4705 } 4706 } 4707 4708 4709 template<class T> 4710 static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) { 4711 int to_index = GetSequenceIndexFromFastElementsKind( 4712 TERMINAL_FAST_ELEMENTS_KIND); 4713 for (int i = 0; i <= to_index; ++i) { 4714 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i); 4715 T stub(isolate, kind); 4716 stub.GetCode(); 4717 if (AllocationSite::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE) { 4718 T stub1(isolate, kind, DISABLE_ALLOCATION_SITES); 4719 stub1.GetCode(); 4720 } 4721 } 4722 } 4723 4724 4725 void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) { 4726 ArrayConstructorStubAheadOfTimeHelper<ArrayNoArgumentConstructorStub>( 4727 isolate); 4728 ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>( 4729 isolate); 4730 ArrayConstructorStubAheadOfTimeHelper<ArrayNArgumentsConstructorStub>( 4731 isolate); 4732 } 4733 4734 4735 void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime( 4736 Isolate* isolate) { 4737 ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS }; 4738 for (int i = 0; i < 2; i++) { 4739 // For internal arrays we only need a few things 4740 InternalArrayNoArgumentConstructorStub stubh1(isolate, kinds[i]); 4741 stubh1.GetCode(); 4742 InternalArraySingleArgumentConstructorStub stubh2(isolate, kinds[i]); 4743 stubh2.GetCode(); 4744 InternalArrayNArgumentsConstructorStub stubh3(isolate, kinds[i]); 4745 stubh3.GetCode(); 4746 } 4747 } 4748 4749 4750 void ArrayConstructorStub::GenerateDispatchToArrayStub( 4751 MacroAssembler* masm, 4752 AllocationSiteOverrideMode mode) { 4753 Register argc = x0; 4754 if (argument_count() == ANY) { 4755 Label zero_case, n_case; 4756 __ Cbz(argc, &zero_case); 4757 __ Cmp(argc, 1); 4758 __ B(ne, &n_case); 4759 4760 // One argument. 4761 CreateArrayDispatchOneArgument(masm, mode); 4762 4763 __ Bind(&zero_case); 4764 // No arguments. 4765 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm, mode); 4766 4767 __ Bind(&n_case); 4768 // N arguments. 4769 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm, mode); 4770 4771 } else if (argument_count() == NONE) { 4772 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm, mode); 4773 } else if (argument_count() == ONE) { 4774 CreateArrayDispatchOneArgument(masm, mode); 4775 } else if (argument_count() == MORE_THAN_ONE) { 4776 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm, mode); 4777 } else { 4778 UNREACHABLE(); 4779 } 4780 } 4781 4782 4783 void ArrayConstructorStub::Generate(MacroAssembler* masm) { 4784 ASM_LOCATION("ArrayConstructorStub::Generate"); 4785 // ----------- S t a t e ------------- 4786 // -- x0 : argc (only if argument_count() == ANY) 4787 // -- x1 : constructor 4788 // -- x2 : AllocationSite or undefined 4789 // -- sp[0] : return address 4790 // -- sp[4] : last argument 4791 // ----------------------------------- 4792 Register constructor = x1; 4793 Register allocation_site = x2; 4794 4795 if (FLAG_debug_code) { 4796 // The array construct code is only set for the global and natives 4797 // builtin Array functions which always have maps. 4798 4799 Label unexpected_map, map_ok; 4800 // Initial map for the builtin Array function should be a map. 4801 __ Ldr(x10, FieldMemOperand(constructor, 4802 JSFunction::kPrototypeOrInitialMapOffset)); 4803 // Will both indicate a NULL and a Smi. 4804 __ JumpIfSmi(x10, &unexpected_map); 4805 __ JumpIfObjectType(x10, x10, x11, MAP_TYPE, &map_ok); 4806 __ Bind(&unexpected_map); 4807 __ Abort(kUnexpectedInitialMapForArrayFunction); 4808 __ Bind(&map_ok); 4809 4810 // We should either have undefined in the allocation_site register or a 4811 // valid AllocationSite. 4812 __ AssertUndefinedOrAllocationSite(allocation_site, x10); 4813 } 4814 4815 Register kind = x3; 4816 Label no_info; 4817 // Get the elements kind and case on that. 4818 __ JumpIfRoot(allocation_site, Heap::kUndefinedValueRootIndex, &no_info); 4819 4820 __ Ldrsw(kind, 4821 UntagSmiFieldMemOperand(allocation_site, 4822 AllocationSite::kTransitionInfoOffset)); 4823 __ And(kind, kind, AllocationSite::ElementsKindBits::kMask); 4824 GenerateDispatchToArrayStub(masm, DONT_OVERRIDE); 4825 4826 __ Bind(&no_info); 4827 GenerateDispatchToArrayStub(masm, DISABLE_ALLOCATION_SITES); 4828 } 4829 4830 4831 void InternalArrayConstructorStub::GenerateCase( 4832 MacroAssembler* masm, ElementsKind kind) { 4833 Label zero_case, n_case; 4834 Register argc = x0; 4835 4836 __ Cbz(argc, &zero_case); 4837 __ CompareAndBranch(argc, 1, ne, &n_case); 4838 4839 // One argument. 4840 if (IsFastPackedElementsKind(kind)) { 4841 Label packed_case; 4842 4843 // We might need to create a holey array; look at the first argument. 4844 __ Peek(x10, 0); 4845 __ Cbz(x10, &packed_case); 4846 4847 InternalArraySingleArgumentConstructorStub 4848 stub1_holey(isolate(), GetHoleyElementsKind(kind)); 4849 __ TailCallStub(&stub1_holey); 4850 4851 __ Bind(&packed_case); 4852 } 4853 InternalArraySingleArgumentConstructorStub stub1(isolate(), kind); 4854 __ TailCallStub(&stub1); 4855 4856 __ Bind(&zero_case); 4857 // No arguments. 4858 InternalArrayNoArgumentConstructorStub stub0(isolate(), kind); 4859 __ TailCallStub(&stub0); 4860 4861 __ Bind(&n_case); 4862 // N arguments. 4863 InternalArrayNArgumentsConstructorStub stubN(isolate(), kind); 4864 __ TailCallStub(&stubN); 4865 } 4866 4867 4868 void InternalArrayConstructorStub::Generate(MacroAssembler* masm) { 4869 // ----------- S t a t e ------------- 4870 // -- x0 : argc 4871 // -- x1 : constructor 4872 // -- sp[0] : return address 4873 // -- sp[4] : last argument 4874 // ----------------------------------- 4875 4876 Register constructor = x1; 4877 4878 if (FLAG_debug_code) { 4879 // The array construct code is only set for the global and natives 4880 // builtin Array functions which always have maps. 4881 4882 Label unexpected_map, map_ok; 4883 // Initial map for the builtin Array function should be a map. 4884 __ Ldr(x10, FieldMemOperand(constructor, 4885 JSFunction::kPrototypeOrInitialMapOffset)); 4886 // Will both indicate a NULL and a Smi. 4887 __ JumpIfSmi(x10, &unexpected_map); 4888 __ JumpIfObjectType(x10, x10, x11, MAP_TYPE, &map_ok); 4889 __ Bind(&unexpected_map); 4890 __ Abort(kUnexpectedInitialMapForArrayFunction); 4891 __ Bind(&map_ok); 4892 } 4893 4894 Register kind = w3; 4895 // Figure out the right elements kind 4896 __ Ldr(x10, FieldMemOperand(constructor, 4897 JSFunction::kPrototypeOrInitialMapOffset)); 4898 4899 // Retrieve elements_kind from map. 4900 __ LoadElementsKindFromMap(kind, x10); 4901 4902 if (FLAG_debug_code) { 4903 Label done; 4904 __ Cmp(x3, FAST_ELEMENTS); 4905 __ Ccmp(x3, FAST_HOLEY_ELEMENTS, ZFlag, ne); 4906 __ Assert(eq, kInvalidElementsKindForInternalArrayOrInternalPackedArray); 4907 } 4908 4909 Label fast_elements_case; 4910 __ CompareAndBranch(kind, FAST_ELEMENTS, eq, &fast_elements_case); 4911 GenerateCase(masm, FAST_HOLEY_ELEMENTS); 4912 4913 __ Bind(&fast_elements_case); 4914 GenerateCase(masm, FAST_ELEMENTS); 4915 } 4916 4917 4918 void CallApiFunctionStub::Generate(MacroAssembler* masm) { 4919 // ----------- S t a t e ------------- 4920 // -- x0 : callee 4921 // -- x4 : call_data 4922 // -- x2 : holder 4923 // -- x1 : api_function_address 4924 // -- cp : context 4925 // -- 4926 // -- sp[0] : last argument 4927 // -- ... 4928 // -- sp[(argc - 1) * 8] : first argument 4929 // -- sp[argc * 8] : receiver 4930 // ----------------------------------- 4931 4932 Register callee = x0; 4933 Register call_data = x4; 4934 Register holder = x2; 4935 Register api_function_address = x1; 4936 Register context = cp; 4937 4938 int argc = this->argc(); 4939 bool is_store = this->is_store(); 4940 bool call_data_undefined = this->call_data_undefined(); 4941 4942 typedef FunctionCallbackArguments FCA; 4943 4944 STATIC_ASSERT(FCA::kContextSaveIndex == 6); 4945 STATIC_ASSERT(FCA::kCalleeIndex == 5); 4946 STATIC_ASSERT(FCA::kDataIndex == 4); 4947 STATIC_ASSERT(FCA::kReturnValueOffset == 3); 4948 STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2); 4949 STATIC_ASSERT(FCA::kIsolateIndex == 1); 4950 STATIC_ASSERT(FCA::kHolderIndex == 0); 4951 STATIC_ASSERT(FCA::kArgsLength == 7); 4952 4953 // FunctionCallbackArguments: context, callee and call data. 4954 __ Push(context, callee, call_data); 4955 4956 // Load context from callee 4957 __ Ldr(context, FieldMemOperand(callee, JSFunction::kContextOffset)); 4958 4959 if (!call_data_undefined) { 4960 __ LoadRoot(call_data, Heap::kUndefinedValueRootIndex); 4961 } 4962 Register isolate_reg = x5; 4963 __ Mov(isolate_reg, ExternalReference::isolate_address(isolate())); 4964 4965 // FunctionCallbackArguments: 4966 // return value, return value default, isolate, holder. 4967 __ Push(call_data, call_data, isolate_reg, holder); 4968 4969 // Prepare arguments. 4970 Register args = x6; 4971 __ Mov(args, masm->StackPointer()); 4972 4973 // Allocate the v8::Arguments structure in the arguments' space, since it's 4974 // not controlled by GC. 4975 const int kApiStackSpace = 4; 4976 4977 // Allocate space for CallApiFunctionAndReturn can store some scratch 4978 // registeres on the stack. 4979 const int kCallApiFunctionSpillSpace = 4; 4980 4981 FrameScope frame_scope(masm, StackFrame::MANUAL); 4982 __ EnterExitFrame(false, x10, kApiStackSpace + kCallApiFunctionSpillSpace); 4983 4984 DCHECK(!AreAliased(x0, api_function_address)); 4985 // x0 = FunctionCallbackInfo& 4986 // Arguments is after the return address. 4987 __ Add(x0, masm->StackPointer(), 1 * kPointerSize); 4988 // FunctionCallbackInfo::implicit_args_ and FunctionCallbackInfo::values_ 4989 __ Add(x10, args, Operand((FCA::kArgsLength - 1 + argc) * kPointerSize)); 4990 __ Stp(args, x10, MemOperand(x0, 0 * kPointerSize)); 4991 // FunctionCallbackInfo::length_ = argc and 4992 // FunctionCallbackInfo::is_construct_call = 0 4993 __ Mov(x10, argc); 4994 __ Stp(x10, xzr, MemOperand(x0, 2 * kPointerSize)); 4995 4996 const int kStackUnwindSpace = argc + FCA::kArgsLength + 1; 4997 ExternalReference thunk_ref = 4998 ExternalReference::invoke_function_callback(isolate()); 4999 5000 AllowExternalCallThatCantCauseGC scope(masm); 5001 MemOperand context_restore_operand( 5002 fp, (2 + FCA::kContextSaveIndex) * kPointerSize); 5003 // Stores return the first js argument 5004 int return_value_offset = 0; 5005 if (is_store) { 5006 return_value_offset = 2 + FCA::kArgsLength; 5007 } else { 5008 return_value_offset = 2 + FCA::kReturnValueOffset; 5009 } 5010 MemOperand return_value_operand(fp, return_value_offset * kPointerSize); 5011 5012 const int spill_offset = 1 + kApiStackSpace; 5013 __ CallApiFunctionAndReturn(api_function_address, 5014 thunk_ref, 5015 kStackUnwindSpace, 5016 spill_offset, 5017 return_value_operand, 5018 &context_restore_operand); 5019 } 5020 5021 5022 void CallApiGetterStub::Generate(MacroAssembler* masm) { 5023 // ----------- S t a t e ------------- 5024 // -- sp[0] : name 5025 // -- sp[8 - kArgsLength*8] : PropertyCallbackArguments object 5026 // -- ... 5027 // -- x2 : api_function_address 5028 // ----------------------------------- 5029 5030 Register api_function_address = ApiGetterDescriptor::function_address(); 5031 DCHECK(api_function_address.is(x2)); 5032 5033 __ Mov(x0, masm->StackPointer()); // x0 = Handle<Name> 5034 __ Add(x1, x0, 1 * kPointerSize); // x1 = PCA 5035 5036 const int kApiStackSpace = 1; 5037 5038 // Allocate space for CallApiFunctionAndReturn can store some scratch 5039 // registeres on the stack. 5040 const int kCallApiFunctionSpillSpace = 4; 5041 5042 FrameScope frame_scope(masm, StackFrame::MANUAL); 5043 __ EnterExitFrame(false, x10, kApiStackSpace + kCallApiFunctionSpillSpace); 5044 5045 // Create PropertyAccessorInfo instance on the stack above the exit frame with 5046 // x1 (internal::Object** args_) as the data. 5047 __ Poke(x1, 1 * kPointerSize); 5048 __ Add(x1, masm->StackPointer(), 1 * kPointerSize); // x1 = AccessorInfo& 5049 5050 const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1; 5051 5052 ExternalReference thunk_ref = 5053 ExternalReference::invoke_accessor_getter_callback(isolate()); 5054 5055 const int spill_offset = 1 + kApiStackSpace; 5056 __ CallApiFunctionAndReturn(api_function_address, 5057 thunk_ref, 5058 kStackUnwindSpace, 5059 spill_offset, 5060 MemOperand(fp, 6 * kPointerSize), 5061 NULL); 5062 } 5063 5064 5065 #undef __ 5066 5067 } } // namespace v8::internal 5068 5069 #endif // V8_TARGET_ARCH_ARM64 5070