1 // Copyright 2016 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 #include "src/code-stub-assembler.h" 5 #include "src/code-factory.h" 6 #include "src/frames-inl.h" 7 #include "src/frames.h" 8 9 namespace v8 { 10 namespace internal { 11 12 using compiler::Node; 13 14 CodeStubAssembler::CodeStubAssembler(compiler::CodeAssemblerState* state) 15 : compiler::CodeAssembler(state) { 16 if (DEBUG_BOOL && FLAG_csa_trap_on_node != nullptr) { 17 HandleBreakOnNode(); 18 } 19 } 20 21 void CodeStubAssembler::HandleBreakOnNode() { 22 // FLAG_csa_trap_on_node should be in a form "STUB,NODE" where STUB is a 23 // string specifying the name of a stub and NODE is number specifying node id. 24 const char* name = state()->name(); 25 size_t name_length = strlen(name); 26 if (strncmp(FLAG_csa_trap_on_node, name, name_length) != 0) { 27 // Different name. 28 return; 29 } 30 size_t option_length = strlen(FLAG_csa_trap_on_node); 31 if (option_length < name_length + 2 || 32 FLAG_csa_trap_on_node[name_length] != ',') { 33 // Option is too short. 34 return; 35 } 36 const char* start = &FLAG_csa_trap_on_node[name_length + 1]; 37 char* end; 38 int node_id = static_cast<int>(strtol(start, &end, 10)); 39 if (start == end) { 40 // Bad node id. 41 return; 42 } 43 BreakOnNode(node_id); 44 } 45 46 void CodeStubAssembler::Assert(const NodeGenerator& codition_body, 47 const char* message, const char* file, 48 int line) { 49 #if defined(DEBUG) 50 if (FLAG_debug_code) { 51 Label ok(this); 52 Label not_ok(this, Label::kDeferred); 53 if (message != nullptr && FLAG_code_comments) { 54 Comment("[ Assert: %s", message); 55 } else { 56 Comment("[ Assert"); 57 } 58 Node* condition = codition_body(); 59 DCHECK_NOT_NULL(condition); 60 Branch(condition, &ok, ¬_ok); 61 Bind(¬_ok); 62 if (message != nullptr) { 63 char chars[1024]; 64 Vector<char> buffer(chars); 65 if (file != nullptr) { 66 SNPrintF(buffer, "CSA_ASSERT failed: %s [%s:%d]\n", message, file, 67 line); 68 } else { 69 SNPrintF(buffer, "CSA_ASSERT failed: %s\n", message); 70 } 71 CallRuntime( 72 Runtime::kGlobalPrint, SmiConstant(Smi::kZero), 73 HeapConstant(factory()->NewStringFromAsciiChecked(&(buffer[0])))); 74 } 75 DebugBreak(); 76 Goto(&ok); 77 Bind(&ok); 78 Comment("] Assert"); 79 } 80 #endif 81 } 82 83 Node* CodeStubAssembler::Select(Node* condition, const NodeGenerator& true_body, 84 const NodeGenerator& false_body, 85 MachineRepresentation rep) { 86 Variable value(this, rep); 87 Label vtrue(this), vfalse(this), end(this); 88 Branch(condition, &vtrue, &vfalse); 89 90 Bind(&vtrue); 91 { 92 value.Bind(true_body()); 93 Goto(&end); 94 } 95 Bind(&vfalse); 96 { 97 value.Bind(false_body()); 98 Goto(&end); 99 } 100 101 Bind(&end); 102 return value.value(); 103 } 104 105 Node* CodeStubAssembler::SelectConstant(Node* condition, Node* true_value, 106 Node* false_value, 107 MachineRepresentation rep) { 108 return Select(condition, [=] { return true_value; }, 109 [=] { return false_value; }, rep); 110 } 111 112 Node* CodeStubAssembler::SelectInt32Constant(Node* condition, int true_value, 113 int false_value) { 114 return SelectConstant(condition, Int32Constant(true_value), 115 Int32Constant(false_value), 116 MachineRepresentation::kWord32); 117 } 118 119 Node* CodeStubAssembler::SelectIntPtrConstant(Node* condition, int true_value, 120 int false_value) { 121 return SelectConstant(condition, IntPtrConstant(true_value), 122 IntPtrConstant(false_value), 123 MachineType::PointerRepresentation()); 124 } 125 126 Node* CodeStubAssembler::SelectBooleanConstant(Node* condition) { 127 return SelectConstant(condition, TrueConstant(), FalseConstant(), 128 MachineRepresentation::kTagged); 129 } 130 131 Node* CodeStubAssembler::SelectTaggedConstant(Node* condition, Node* true_value, 132 Node* false_value) { 133 return SelectConstant(condition, true_value, false_value, 134 MachineRepresentation::kTagged); 135 } 136 137 Node* CodeStubAssembler::SelectSmiConstant(Node* condition, Smi* true_value, 138 Smi* false_value) { 139 return SelectConstant(condition, SmiConstant(true_value), 140 SmiConstant(false_value), 141 MachineRepresentation::kTaggedSigned); 142 } 143 144 Node* CodeStubAssembler::NoContextConstant() { return NumberConstant(0); } 145 146 #define HEAP_CONSTANT_ACCESSOR(rootName, name) \ 147 Node* CodeStubAssembler::name##Constant() { \ 148 return LoadRoot(Heap::k##rootName##RootIndex); \ 149 } 150 HEAP_CONSTANT_LIST(HEAP_CONSTANT_ACCESSOR); 151 #undef HEAP_CONSTANT_ACCESSOR 152 153 #define HEAP_CONSTANT_TEST(rootName, name) \ 154 Node* CodeStubAssembler::Is##name(Node* value) { \ 155 return WordEqual(value, name##Constant()); \ 156 } 157 HEAP_CONSTANT_LIST(HEAP_CONSTANT_TEST); 158 #undef HEAP_CONSTANT_TEST 159 160 Node* CodeStubAssembler::HashSeed() { 161 return LoadAndUntagToWord32Root(Heap::kHashSeedRootIndex); 162 } 163 164 Node* CodeStubAssembler::StaleRegisterConstant() { 165 return LoadRoot(Heap::kStaleRegisterRootIndex); 166 } 167 168 Node* CodeStubAssembler::IntPtrOrSmiConstant(int value, ParameterMode mode) { 169 if (mode == SMI_PARAMETERS) { 170 return SmiConstant(Smi::FromInt(value)); 171 } else { 172 DCHECK_EQ(INTPTR_PARAMETERS, mode); 173 return IntPtrConstant(value); 174 } 175 } 176 177 bool CodeStubAssembler::IsIntPtrOrSmiConstantZero(Node* test) { 178 int32_t constant_test; 179 Smi* smi_test; 180 if ((ToInt32Constant(test, constant_test) && constant_test == 0) || 181 (ToSmiConstant(test, smi_test) && smi_test->value() == 0)) { 182 return true; 183 } 184 return false; 185 } 186 187 Node* CodeStubAssembler::IntPtrRoundUpToPowerOfTwo32(Node* value) { 188 Comment("IntPtrRoundUpToPowerOfTwo32"); 189 CSA_ASSERT(this, UintPtrLessThanOrEqual(value, IntPtrConstant(0x80000000u))); 190 value = IntPtrSub(value, IntPtrConstant(1)); 191 for (int i = 1; i <= 16; i *= 2) { 192 value = WordOr(value, WordShr(value, IntPtrConstant(i))); 193 } 194 return IntPtrAdd(value, IntPtrConstant(1)); 195 } 196 197 Node* CodeStubAssembler::WordIsPowerOfTwo(Node* value) { 198 // value && !(value & (value - 1)) 199 return WordEqual( 200 Select( 201 WordEqual(value, IntPtrConstant(0)), 202 [=] { return IntPtrConstant(1); }, 203 [=] { return WordAnd(value, IntPtrSub(value, IntPtrConstant(1))); }, 204 MachineType::PointerRepresentation()), 205 IntPtrConstant(0)); 206 } 207 208 Node* CodeStubAssembler::Float64Round(Node* x) { 209 Node* one = Float64Constant(1.0); 210 Node* one_half = Float64Constant(0.5); 211 212 Label return_x(this); 213 214 // Round up {x} towards Infinity. 215 Variable var_x(this, MachineRepresentation::kFloat64, Float64Ceil(x)); 216 217 GotoIf(Float64LessThanOrEqual(Float64Sub(var_x.value(), one_half), x), 218 &return_x); 219 var_x.Bind(Float64Sub(var_x.value(), one)); 220 Goto(&return_x); 221 222 Bind(&return_x); 223 return var_x.value(); 224 } 225 226 Node* CodeStubAssembler::Float64Ceil(Node* x) { 227 if (IsFloat64RoundUpSupported()) { 228 return Float64RoundUp(x); 229 } 230 231 Node* one = Float64Constant(1.0); 232 Node* zero = Float64Constant(0.0); 233 Node* two_52 = Float64Constant(4503599627370496.0E0); 234 Node* minus_two_52 = Float64Constant(-4503599627370496.0E0); 235 236 Variable var_x(this, MachineRepresentation::kFloat64, x); 237 Label return_x(this), return_minus_x(this); 238 239 // Check if {x} is greater than zero. 240 Label if_xgreaterthanzero(this), if_xnotgreaterthanzero(this); 241 Branch(Float64GreaterThan(x, zero), &if_xgreaterthanzero, 242 &if_xnotgreaterthanzero); 243 244 Bind(&if_xgreaterthanzero); 245 { 246 // Just return {x} unless it's in the range ]0,2^52[. 247 GotoIf(Float64GreaterThanOrEqual(x, two_52), &return_x); 248 249 // Round positive {x} towards Infinity. 250 var_x.Bind(Float64Sub(Float64Add(two_52, x), two_52)); 251 GotoIfNot(Float64LessThan(var_x.value(), x), &return_x); 252 var_x.Bind(Float64Add(var_x.value(), one)); 253 Goto(&return_x); 254 } 255 256 Bind(&if_xnotgreaterthanzero); 257 { 258 // Just return {x} unless it's in the range ]-2^52,0[ 259 GotoIf(Float64LessThanOrEqual(x, minus_two_52), &return_x); 260 GotoIfNot(Float64LessThan(x, zero), &return_x); 261 262 // Round negated {x} towards Infinity and return the result negated. 263 Node* minus_x = Float64Neg(x); 264 var_x.Bind(Float64Sub(Float64Add(two_52, minus_x), two_52)); 265 GotoIfNot(Float64GreaterThan(var_x.value(), minus_x), &return_minus_x); 266 var_x.Bind(Float64Sub(var_x.value(), one)); 267 Goto(&return_minus_x); 268 } 269 270 Bind(&return_minus_x); 271 var_x.Bind(Float64Neg(var_x.value())); 272 Goto(&return_x); 273 274 Bind(&return_x); 275 return var_x.value(); 276 } 277 278 Node* CodeStubAssembler::Float64Floor(Node* x) { 279 if (IsFloat64RoundDownSupported()) { 280 return Float64RoundDown(x); 281 } 282 283 Node* one = Float64Constant(1.0); 284 Node* zero = Float64Constant(0.0); 285 Node* two_52 = Float64Constant(4503599627370496.0E0); 286 Node* minus_two_52 = Float64Constant(-4503599627370496.0E0); 287 288 Variable var_x(this, MachineRepresentation::kFloat64, x); 289 Label return_x(this), return_minus_x(this); 290 291 // Check if {x} is greater than zero. 292 Label if_xgreaterthanzero(this), if_xnotgreaterthanzero(this); 293 Branch(Float64GreaterThan(x, zero), &if_xgreaterthanzero, 294 &if_xnotgreaterthanzero); 295 296 Bind(&if_xgreaterthanzero); 297 { 298 // Just return {x} unless it's in the range ]0,2^52[. 299 GotoIf(Float64GreaterThanOrEqual(x, two_52), &return_x); 300 301 // Round positive {x} towards -Infinity. 302 var_x.Bind(Float64Sub(Float64Add(two_52, x), two_52)); 303 GotoIfNot(Float64GreaterThan(var_x.value(), x), &return_x); 304 var_x.Bind(Float64Sub(var_x.value(), one)); 305 Goto(&return_x); 306 } 307 308 Bind(&if_xnotgreaterthanzero); 309 { 310 // Just return {x} unless it's in the range ]-2^52,0[ 311 GotoIf(Float64LessThanOrEqual(x, minus_two_52), &return_x); 312 GotoIfNot(Float64LessThan(x, zero), &return_x); 313 314 // Round negated {x} towards -Infinity and return the result negated. 315 Node* minus_x = Float64Neg(x); 316 var_x.Bind(Float64Sub(Float64Add(two_52, minus_x), two_52)); 317 GotoIfNot(Float64LessThan(var_x.value(), minus_x), &return_minus_x); 318 var_x.Bind(Float64Add(var_x.value(), one)); 319 Goto(&return_minus_x); 320 } 321 322 Bind(&return_minus_x); 323 var_x.Bind(Float64Neg(var_x.value())); 324 Goto(&return_x); 325 326 Bind(&return_x); 327 return var_x.value(); 328 } 329 330 Node* CodeStubAssembler::Float64RoundToEven(Node* x) { 331 if (IsFloat64RoundTiesEvenSupported()) { 332 return Float64RoundTiesEven(x); 333 } 334 // See ES#sec-touint8clamp for details. 335 Node* f = Float64Floor(x); 336 Node* f_and_half = Float64Add(f, Float64Constant(0.5)); 337 338 Variable var_result(this, MachineRepresentation::kFloat64); 339 Label return_f(this), return_f_plus_one(this), done(this); 340 341 GotoIf(Float64LessThan(f_and_half, x), &return_f_plus_one); 342 GotoIf(Float64LessThan(x, f_and_half), &return_f); 343 { 344 Node* f_mod_2 = Float64Mod(f, Float64Constant(2.0)); 345 Branch(Float64Equal(f_mod_2, Float64Constant(0.0)), &return_f, 346 &return_f_plus_one); 347 } 348 349 Bind(&return_f); 350 var_result.Bind(f); 351 Goto(&done); 352 353 Bind(&return_f_plus_one); 354 var_result.Bind(Float64Add(f, Float64Constant(1.0))); 355 Goto(&done); 356 357 Bind(&done); 358 return var_result.value(); 359 } 360 361 Node* CodeStubAssembler::Float64Trunc(Node* x) { 362 if (IsFloat64RoundTruncateSupported()) { 363 return Float64RoundTruncate(x); 364 } 365 366 Node* one = Float64Constant(1.0); 367 Node* zero = Float64Constant(0.0); 368 Node* two_52 = Float64Constant(4503599627370496.0E0); 369 Node* minus_two_52 = Float64Constant(-4503599627370496.0E0); 370 371 Variable var_x(this, MachineRepresentation::kFloat64, x); 372 Label return_x(this), return_minus_x(this); 373 374 // Check if {x} is greater than 0. 375 Label if_xgreaterthanzero(this), if_xnotgreaterthanzero(this); 376 Branch(Float64GreaterThan(x, zero), &if_xgreaterthanzero, 377 &if_xnotgreaterthanzero); 378 379 Bind(&if_xgreaterthanzero); 380 { 381 if (IsFloat64RoundDownSupported()) { 382 var_x.Bind(Float64RoundDown(x)); 383 } else { 384 // Just return {x} unless it's in the range ]0,2^52[. 385 GotoIf(Float64GreaterThanOrEqual(x, two_52), &return_x); 386 387 // Round positive {x} towards -Infinity. 388 var_x.Bind(Float64Sub(Float64Add(two_52, x), two_52)); 389 GotoIfNot(Float64GreaterThan(var_x.value(), x), &return_x); 390 var_x.Bind(Float64Sub(var_x.value(), one)); 391 } 392 Goto(&return_x); 393 } 394 395 Bind(&if_xnotgreaterthanzero); 396 { 397 if (IsFloat64RoundUpSupported()) { 398 var_x.Bind(Float64RoundUp(x)); 399 Goto(&return_x); 400 } else { 401 // Just return {x} unless its in the range ]-2^52,0[. 402 GotoIf(Float64LessThanOrEqual(x, minus_two_52), &return_x); 403 GotoIfNot(Float64LessThan(x, zero), &return_x); 404 405 // Round negated {x} towards -Infinity and return result negated. 406 Node* minus_x = Float64Neg(x); 407 var_x.Bind(Float64Sub(Float64Add(two_52, minus_x), two_52)); 408 GotoIfNot(Float64GreaterThan(var_x.value(), minus_x), &return_minus_x); 409 var_x.Bind(Float64Sub(var_x.value(), one)); 410 Goto(&return_minus_x); 411 } 412 } 413 414 Bind(&return_minus_x); 415 var_x.Bind(Float64Neg(var_x.value())); 416 Goto(&return_x); 417 418 Bind(&return_x); 419 return var_x.value(); 420 } 421 422 Node* CodeStubAssembler::SmiShiftBitsConstant() { 423 return IntPtrConstant(kSmiShiftSize + kSmiTagSize); 424 } 425 426 Node* CodeStubAssembler::SmiFromWord32(Node* value) { 427 value = ChangeInt32ToIntPtr(value); 428 return BitcastWordToTaggedSigned(WordShl(value, SmiShiftBitsConstant())); 429 } 430 431 Node* CodeStubAssembler::SmiTag(Node* value) { 432 int32_t constant_value; 433 if (ToInt32Constant(value, constant_value) && Smi::IsValid(constant_value)) { 434 return SmiConstant(Smi::FromInt(constant_value)); 435 } 436 return BitcastWordToTaggedSigned(WordShl(value, SmiShiftBitsConstant())); 437 } 438 439 Node* CodeStubAssembler::SmiUntag(Node* value) { 440 return WordSar(BitcastTaggedToWord(value), SmiShiftBitsConstant()); 441 } 442 443 Node* CodeStubAssembler::SmiToWord32(Node* value) { 444 Node* result = SmiUntag(value); 445 return TruncateWordToWord32(result); 446 } 447 448 Node* CodeStubAssembler::SmiToFloat64(Node* value) { 449 return ChangeInt32ToFloat64(SmiToWord32(value)); 450 } 451 452 Node* CodeStubAssembler::SmiMax(Node* a, Node* b) { 453 return SelectTaggedConstant(SmiLessThan(a, b), b, a); 454 } 455 456 Node* CodeStubAssembler::SmiMin(Node* a, Node* b) { 457 return SelectTaggedConstant(SmiLessThan(a, b), a, b); 458 } 459 460 Node* CodeStubAssembler::SmiMod(Node* a, Node* b) { 461 Variable var_result(this, MachineRepresentation::kTagged); 462 Label return_result(this, &var_result), 463 return_minuszero(this, Label::kDeferred), 464 return_nan(this, Label::kDeferred); 465 466 // Untag {a} and {b}. 467 a = SmiToWord32(a); 468 b = SmiToWord32(b); 469 470 // Return NaN if {b} is zero. 471 GotoIf(Word32Equal(b, Int32Constant(0)), &return_nan); 472 473 // Check if {a} is non-negative. 474 Label if_aisnotnegative(this), if_aisnegative(this, Label::kDeferred); 475 Branch(Int32LessThanOrEqual(Int32Constant(0), a), &if_aisnotnegative, 476 &if_aisnegative); 477 478 Bind(&if_aisnotnegative); 479 { 480 // Fast case, don't need to check any other edge cases. 481 Node* r = Int32Mod(a, b); 482 var_result.Bind(SmiFromWord32(r)); 483 Goto(&return_result); 484 } 485 486 Bind(&if_aisnegative); 487 { 488 if (SmiValuesAre32Bits()) { 489 // Check if {a} is kMinInt and {b} is -1 (only relevant if the 490 // kMinInt is actually representable as a Smi). 491 Label join(this); 492 GotoIfNot(Word32Equal(a, Int32Constant(kMinInt)), &join); 493 GotoIf(Word32Equal(b, Int32Constant(-1)), &return_minuszero); 494 Goto(&join); 495 Bind(&join); 496 } 497 498 // Perform the integer modulus operation. 499 Node* r = Int32Mod(a, b); 500 501 // Check if {r} is zero, and if so return -0, because we have to 502 // take the sign of the left hand side {a}, which is negative. 503 GotoIf(Word32Equal(r, Int32Constant(0)), &return_minuszero); 504 505 // The remainder {r} can be outside the valid Smi range on 32bit 506 // architectures, so we cannot just say SmiFromWord32(r) here. 507 var_result.Bind(ChangeInt32ToTagged(r)); 508 Goto(&return_result); 509 } 510 511 Bind(&return_minuszero); 512 var_result.Bind(MinusZeroConstant()); 513 Goto(&return_result); 514 515 Bind(&return_nan); 516 var_result.Bind(NanConstant()); 517 Goto(&return_result); 518 519 Bind(&return_result); 520 return var_result.value(); 521 } 522 523 Node* CodeStubAssembler::SmiMul(Node* a, Node* b) { 524 Variable var_result(this, MachineRepresentation::kTagged); 525 Variable var_lhs_float64(this, MachineRepresentation::kFloat64), 526 var_rhs_float64(this, MachineRepresentation::kFloat64); 527 Label return_result(this, &var_result); 528 529 // Both {a} and {b} are Smis. Convert them to integers and multiply. 530 Node* lhs32 = SmiToWord32(a); 531 Node* rhs32 = SmiToWord32(b); 532 Node* pair = Int32MulWithOverflow(lhs32, rhs32); 533 534 Node* overflow = Projection(1, pair); 535 536 // Check if the multiplication overflowed. 537 Label if_overflow(this, Label::kDeferred), if_notoverflow(this); 538 Branch(overflow, &if_overflow, &if_notoverflow); 539 Bind(&if_notoverflow); 540 { 541 // If the answer is zero, we may need to return -0.0, depending on the 542 // input. 543 Label answer_zero(this), answer_not_zero(this); 544 Node* answer = Projection(0, pair); 545 Node* zero = Int32Constant(0); 546 Branch(Word32Equal(answer, zero), &answer_zero, &answer_not_zero); 547 Bind(&answer_not_zero); 548 { 549 var_result.Bind(ChangeInt32ToTagged(answer)); 550 Goto(&return_result); 551 } 552 Bind(&answer_zero); 553 { 554 Node* or_result = Word32Or(lhs32, rhs32); 555 Label if_should_be_negative_zero(this), if_should_be_zero(this); 556 Branch(Int32LessThan(or_result, zero), &if_should_be_negative_zero, 557 &if_should_be_zero); 558 Bind(&if_should_be_negative_zero); 559 { 560 var_result.Bind(MinusZeroConstant()); 561 Goto(&return_result); 562 } 563 Bind(&if_should_be_zero); 564 { 565 var_result.Bind(SmiConstant(0)); 566 Goto(&return_result); 567 } 568 } 569 } 570 Bind(&if_overflow); 571 { 572 var_lhs_float64.Bind(SmiToFloat64(a)); 573 var_rhs_float64.Bind(SmiToFloat64(b)); 574 Node* value = Float64Mul(var_lhs_float64.value(), var_rhs_float64.value()); 575 Node* result = AllocateHeapNumberWithValue(value); 576 var_result.Bind(result); 577 Goto(&return_result); 578 } 579 580 Bind(&return_result); 581 return var_result.value(); 582 } 583 584 Node* CodeStubAssembler::TruncateWordToWord32(Node* value) { 585 if (Is64()) { 586 return TruncateInt64ToInt32(value); 587 } 588 return value; 589 } 590 591 Node* CodeStubAssembler::TaggedIsSmi(Node* a) { 592 return WordEqual(WordAnd(BitcastTaggedToWord(a), IntPtrConstant(kSmiTagMask)), 593 IntPtrConstant(0)); 594 } 595 596 Node* CodeStubAssembler::TaggedIsNotSmi(Node* a) { 597 return WordNotEqual( 598 WordAnd(BitcastTaggedToWord(a), IntPtrConstant(kSmiTagMask)), 599 IntPtrConstant(0)); 600 } 601 602 Node* CodeStubAssembler::TaggedIsPositiveSmi(Node* a) { 603 return WordEqual(WordAnd(BitcastTaggedToWord(a), 604 IntPtrConstant(kSmiTagMask | kSmiSignMask)), 605 IntPtrConstant(0)); 606 } 607 608 Node* CodeStubAssembler::WordIsWordAligned(Node* word) { 609 return WordEqual(IntPtrConstant(0), 610 WordAnd(word, IntPtrConstant((1 << kPointerSizeLog2) - 1))); 611 } 612 613 void CodeStubAssembler::BranchIfPrototypesHaveNoElements( 614 Node* receiver_map, Label* definitely_no_elements, 615 Label* possibly_elements) { 616 Variable var_map(this, MachineRepresentation::kTagged, receiver_map); 617 Label loop_body(this, &var_map); 618 Node* empty_elements = LoadRoot(Heap::kEmptyFixedArrayRootIndex); 619 Goto(&loop_body); 620 621 Bind(&loop_body); 622 { 623 Node* map = var_map.value(); 624 Node* prototype = LoadMapPrototype(map); 625 GotoIf(WordEqual(prototype, NullConstant()), definitely_no_elements); 626 Node* prototype_map = LoadMap(prototype); 627 // Pessimistically assume elements if a Proxy, Special API Object, 628 // or JSValue wrapper is found on the prototype chain. After this 629 // instance type check, it's not necessary to check for interceptors or 630 // access checks. 631 GotoIf(Int32LessThanOrEqual(LoadMapInstanceType(prototype_map), 632 Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)), 633 possibly_elements); 634 GotoIf(WordNotEqual(LoadElements(prototype), empty_elements), 635 possibly_elements); 636 var_map.Bind(prototype_map); 637 Goto(&loop_body); 638 } 639 } 640 641 void CodeStubAssembler::BranchIfJSReceiver(Node* object, Label* if_true, 642 Label* if_false) { 643 GotoIf(TaggedIsSmi(object), if_false); 644 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 645 Branch(Int32GreaterThanOrEqual(LoadInstanceType(object), 646 Int32Constant(FIRST_JS_RECEIVER_TYPE)), 647 if_true, if_false); 648 } 649 650 void CodeStubAssembler::BranchIfJSObject(Node* object, Label* if_true, 651 Label* if_false) { 652 GotoIf(TaggedIsSmi(object), if_false); 653 STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); 654 Branch(Int32GreaterThanOrEqual(LoadInstanceType(object), 655 Int32Constant(FIRST_JS_OBJECT_TYPE)), 656 if_true, if_false); 657 } 658 659 void CodeStubAssembler::BranchIfFastJSArray( 660 Node* object, Node* context, CodeStubAssembler::FastJSArrayAccessMode mode, 661 Label* if_true, Label* if_false) { 662 // Bailout if receiver is a Smi. 663 GotoIf(TaggedIsSmi(object), if_false); 664 665 Node* map = LoadMap(object); 666 667 // Bailout if instance type is not JS_ARRAY_TYPE. 668 GotoIf(Word32NotEqual(LoadMapInstanceType(map), Int32Constant(JS_ARRAY_TYPE)), 669 if_false); 670 671 Node* elements_kind = LoadMapElementsKind(map); 672 673 // Bailout if receiver has slow elements. 674 GotoIfNot(IsFastElementsKind(elements_kind), if_false); 675 676 // Check prototype chain if receiver does not have packed elements. 677 if (mode == FastJSArrayAccessMode::INBOUNDS_READ) { 678 GotoIfNot(IsHoleyFastElementsKind(elements_kind), if_true); 679 } 680 BranchIfPrototypesHaveNoElements(map, if_true, if_false); 681 } 682 683 Node* CodeStubAssembler::AllocateRawUnaligned(Node* size_in_bytes, 684 AllocationFlags flags, 685 Node* top_address, 686 Node* limit_address) { 687 Node* top = Load(MachineType::Pointer(), top_address); 688 Node* limit = Load(MachineType::Pointer(), limit_address); 689 690 // If there's not enough space, call the runtime. 691 Variable result(this, MachineRepresentation::kTagged); 692 Label runtime_call(this, Label::kDeferred), no_runtime_call(this); 693 Label merge_runtime(this, &result); 694 695 if (flags & kAllowLargeObjectAllocation) { 696 Label next(this); 697 GotoIf(IsRegularHeapObjectSize(size_in_bytes), &next); 698 699 Node* runtime_flags = SmiConstant( 700 Smi::FromInt(AllocateDoubleAlignFlag::encode(false) | 701 AllocateTargetSpace::encode(AllocationSpace::LO_SPACE))); 702 Node* const runtime_result = 703 CallRuntime(Runtime::kAllocateInTargetSpace, NoContextConstant(), 704 SmiTag(size_in_bytes), runtime_flags); 705 result.Bind(runtime_result); 706 Goto(&merge_runtime); 707 708 Bind(&next); 709 } 710 711 Node* new_top = IntPtrAdd(top, size_in_bytes); 712 Branch(UintPtrGreaterThanOrEqual(new_top, limit), &runtime_call, 713 &no_runtime_call); 714 715 Bind(&runtime_call); 716 Node* runtime_result; 717 if (flags & kPretenured) { 718 Node* runtime_flags = SmiConstant( 719 Smi::FromInt(AllocateDoubleAlignFlag::encode(false) | 720 AllocateTargetSpace::encode(AllocationSpace::OLD_SPACE))); 721 runtime_result = 722 CallRuntime(Runtime::kAllocateInTargetSpace, NoContextConstant(), 723 SmiTag(size_in_bytes), runtime_flags); 724 } else { 725 runtime_result = CallRuntime(Runtime::kAllocateInNewSpace, 726 NoContextConstant(), SmiTag(size_in_bytes)); 727 } 728 result.Bind(runtime_result); 729 Goto(&merge_runtime); 730 731 // When there is enough space, return `top' and bump it up. 732 Bind(&no_runtime_call); 733 Node* no_runtime_result = top; 734 StoreNoWriteBarrier(MachineType::PointerRepresentation(), top_address, 735 new_top); 736 no_runtime_result = BitcastWordToTagged( 737 IntPtrAdd(no_runtime_result, IntPtrConstant(kHeapObjectTag))); 738 result.Bind(no_runtime_result); 739 Goto(&merge_runtime); 740 741 Bind(&merge_runtime); 742 return result.value(); 743 } 744 745 Node* CodeStubAssembler::AllocateRawAligned(Node* size_in_bytes, 746 AllocationFlags flags, 747 Node* top_address, 748 Node* limit_address) { 749 Node* top = Load(MachineType::Pointer(), top_address); 750 Node* limit = Load(MachineType::Pointer(), limit_address); 751 Variable adjusted_size(this, MachineType::PointerRepresentation(), 752 size_in_bytes); 753 if (flags & kDoubleAlignment) { 754 Label aligned(this), not_aligned(this), merge(this, &adjusted_size); 755 Branch(WordAnd(top, IntPtrConstant(kDoubleAlignmentMask)), ¬_aligned, 756 &aligned); 757 758 Bind(¬_aligned); 759 Node* not_aligned_size = 760 IntPtrAdd(size_in_bytes, IntPtrConstant(kPointerSize)); 761 adjusted_size.Bind(not_aligned_size); 762 Goto(&merge); 763 764 Bind(&aligned); 765 Goto(&merge); 766 767 Bind(&merge); 768 } 769 770 Variable address( 771 this, MachineRepresentation::kTagged, 772 AllocateRawUnaligned(adjusted_size.value(), kNone, top, limit)); 773 774 Label needs_filler(this), doesnt_need_filler(this), 775 merge_address(this, &address); 776 Branch(IntPtrEqual(adjusted_size.value(), size_in_bytes), &doesnt_need_filler, 777 &needs_filler); 778 779 Bind(&needs_filler); 780 // Store a filler and increase the address by kPointerSize. 781 StoreNoWriteBarrier(MachineType::PointerRepresentation(), top, 782 LoadRoot(Heap::kOnePointerFillerMapRootIndex)); 783 address.Bind(BitcastWordToTagged( 784 IntPtrAdd(address.value(), IntPtrConstant(kPointerSize)))); 785 Goto(&merge_address); 786 787 Bind(&doesnt_need_filler); 788 Goto(&merge_address); 789 790 Bind(&merge_address); 791 // Update the top. 792 StoreNoWriteBarrier(MachineType::PointerRepresentation(), top_address, 793 IntPtrAdd(top, adjusted_size.value())); 794 return address.value(); 795 } 796 797 Node* CodeStubAssembler::Allocate(Node* size_in_bytes, AllocationFlags flags) { 798 Comment("Allocate"); 799 bool const new_space = !(flags & kPretenured); 800 Node* top_address = ExternalConstant( 801 new_space 802 ? ExternalReference::new_space_allocation_top_address(isolate()) 803 : ExternalReference::old_space_allocation_top_address(isolate())); 804 DCHECK_EQ(kPointerSize, 805 ExternalReference::new_space_allocation_limit_address(isolate()) 806 .address() - 807 ExternalReference::new_space_allocation_top_address(isolate()) 808 .address()); 809 DCHECK_EQ(kPointerSize, 810 ExternalReference::old_space_allocation_limit_address(isolate()) 811 .address() - 812 ExternalReference::old_space_allocation_top_address(isolate()) 813 .address()); 814 Node* limit_address = IntPtrAdd(top_address, IntPtrConstant(kPointerSize)); 815 816 #ifdef V8_HOST_ARCH_32_BIT 817 if (flags & kDoubleAlignment) { 818 return AllocateRawAligned(size_in_bytes, flags, top_address, limit_address); 819 } 820 #endif 821 822 return AllocateRawUnaligned(size_in_bytes, flags, top_address, limit_address); 823 } 824 825 Node* CodeStubAssembler::Allocate(int size_in_bytes, AllocationFlags flags) { 826 return CodeStubAssembler::Allocate(IntPtrConstant(size_in_bytes), flags); 827 } 828 829 Node* CodeStubAssembler::InnerAllocate(Node* previous, Node* offset) { 830 return BitcastWordToTagged(IntPtrAdd(BitcastTaggedToWord(previous), offset)); 831 } 832 833 Node* CodeStubAssembler::InnerAllocate(Node* previous, int offset) { 834 return InnerAllocate(previous, IntPtrConstant(offset)); 835 } 836 837 Node* CodeStubAssembler::IsRegularHeapObjectSize(Node* size) { 838 return UintPtrLessThanOrEqual(size, 839 IntPtrConstant(kMaxRegularHeapObjectSize)); 840 } 841 842 void CodeStubAssembler::BranchIfToBooleanIsTrue(Node* value, Label* if_true, 843 Label* if_false) { 844 Label if_valueissmi(this), if_valueisnotsmi(this), 845 if_valueisheapnumber(this, Label::kDeferred); 846 847 // Rule out false {value}. 848 GotoIf(WordEqual(value, BooleanConstant(false)), if_false); 849 850 // Check if {value} is a Smi or a HeapObject. 851 Branch(TaggedIsSmi(value), &if_valueissmi, &if_valueisnotsmi); 852 853 Bind(&if_valueissmi); 854 { 855 // The {value} is a Smi, only need to check against zero. 856 BranchIfSmiEqual(value, SmiConstant(0), if_false, if_true); 857 } 858 859 Bind(&if_valueisnotsmi); 860 { 861 // Check if {value} is the empty string. 862 GotoIf(IsEmptyString(value), if_false); 863 864 // The {value} is a HeapObject, load its map. 865 Node* value_map = LoadMap(value); 866 867 // Only null, undefined and document.all have the undetectable bit set, 868 // so we can return false immediately when that bit is set. 869 Node* value_map_bitfield = LoadMapBitField(value_map); 870 Node* value_map_undetectable = 871 Word32And(value_map_bitfield, Int32Constant(1 << Map::kIsUndetectable)); 872 873 // Check if the {value} is undetectable. 874 GotoIfNot(Word32Equal(value_map_undetectable, Int32Constant(0)), if_false); 875 876 // We still need to handle numbers specially, but all other {value}s 877 // that make it here yield true. 878 Branch(IsHeapNumberMap(value_map), &if_valueisheapnumber, if_true); 879 880 Bind(&if_valueisheapnumber); 881 { 882 // Load the floating point value of {value}. 883 Node* value_value = LoadObjectField(value, HeapNumber::kValueOffset, 884 MachineType::Float64()); 885 886 // Check if the floating point {value} is neither 0.0, -0.0 nor NaN. 887 Branch(Float64LessThan(Float64Constant(0.0), Float64Abs(value_value)), 888 if_true, if_false); 889 } 890 } 891 } 892 893 Node* CodeStubAssembler::LoadFromFrame(int offset, MachineType rep) { 894 Node* frame_pointer = LoadFramePointer(); 895 return Load(rep, frame_pointer, IntPtrConstant(offset)); 896 } 897 898 Node* CodeStubAssembler::LoadFromParentFrame(int offset, MachineType rep) { 899 Node* frame_pointer = LoadParentFramePointer(); 900 return Load(rep, frame_pointer, IntPtrConstant(offset)); 901 } 902 903 Node* CodeStubAssembler::LoadBufferObject(Node* buffer, int offset, 904 MachineType rep) { 905 return Load(rep, buffer, IntPtrConstant(offset)); 906 } 907 908 Node* CodeStubAssembler::LoadObjectField(Node* object, int offset, 909 MachineType rep) { 910 return Load(rep, object, IntPtrConstant(offset - kHeapObjectTag)); 911 } 912 913 Node* CodeStubAssembler::LoadObjectField(Node* object, Node* offset, 914 MachineType rep) { 915 return Load(rep, object, IntPtrSub(offset, IntPtrConstant(kHeapObjectTag))); 916 } 917 918 Node* CodeStubAssembler::LoadAndUntagObjectField(Node* object, int offset) { 919 if (Is64()) { 920 #if V8_TARGET_LITTLE_ENDIAN 921 offset += kPointerSize / 2; 922 #endif 923 return ChangeInt32ToInt64( 924 LoadObjectField(object, offset, MachineType::Int32())); 925 } else { 926 return SmiToWord(LoadObjectField(object, offset, MachineType::AnyTagged())); 927 } 928 } 929 930 Node* CodeStubAssembler::LoadAndUntagToWord32ObjectField(Node* object, 931 int offset) { 932 if (Is64()) { 933 #if V8_TARGET_LITTLE_ENDIAN 934 offset += kPointerSize / 2; 935 #endif 936 return LoadObjectField(object, offset, MachineType::Int32()); 937 } else { 938 return SmiToWord32( 939 LoadObjectField(object, offset, MachineType::AnyTagged())); 940 } 941 } 942 943 Node* CodeStubAssembler::LoadAndUntagSmi(Node* base, int index) { 944 if (Is64()) { 945 #if V8_TARGET_LITTLE_ENDIAN 946 index += kPointerSize / 2; 947 #endif 948 return ChangeInt32ToInt64( 949 Load(MachineType::Int32(), base, IntPtrConstant(index))); 950 } else { 951 return SmiToWord( 952 Load(MachineType::AnyTagged(), base, IntPtrConstant(index))); 953 } 954 } 955 956 Node* CodeStubAssembler::LoadAndUntagToWord32Root( 957 Heap::RootListIndex root_index) { 958 Node* roots_array_start = 959 ExternalConstant(ExternalReference::roots_array_start(isolate())); 960 int index = root_index * kPointerSize; 961 if (Is64()) { 962 #if V8_TARGET_LITTLE_ENDIAN 963 index += kPointerSize / 2; 964 #endif 965 return Load(MachineType::Int32(), roots_array_start, IntPtrConstant(index)); 966 } else { 967 return SmiToWord32(Load(MachineType::AnyTagged(), roots_array_start, 968 IntPtrConstant(index))); 969 } 970 } 971 972 Node* CodeStubAssembler::StoreAndTagSmi(Node* base, int offset, Node* value) { 973 if (Is64()) { 974 int zero_offset = offset + kPointerSize / 2; 975 int payload_offset = offset; 976 #if V8_TARGET_LITTLE_ENDIAN 977 std::swap(zero_offset, payload_offset); 978 #endif 979 StoreNoWriteBarrier(MachineRepresentation::kWord32, base, 980 IntPtrConstant(zero_offset), Int32Constant(0)); 981 return StoreNoWriteBarrier(MachineRepresentation::kWord32, base, 982 IntPtrConstant(payload_offset), 983 TruncateInt64ToInt32(value)); 984 } else { 985 return StoreNoWriteBarrier(MachineRepresentation::kTaggedSigned, base, 986 IntPtrConstant(offset), SmiTag(value)); 987 } 988 } 989 990 Node* CodeStubAssembler::LoadHeapNumberValue(Node* object) { 991 return LoadObjectField(object, HeapNumber::kValueOffset, 992 MachineType::Float64()); 993 } 994 995 Node* CodeStubAssembler::LoadMap(Node* object) { 996 return LoadObjectField(object, HeapObject::kMapOffset); 997 } 998 999 Node* CodeStubAssembler::LoadInstanceType(Node* object) { 1000 return LoadMapInstanceType(LoadMap(object)); 1001 } 1002 1003 Node* CodeStubAssembler::HasInstanceType(Node* object, 1004 InstanceType instance_type) { 1005 return Word32Equal(LoadInstanceType(object), Int32Constant(instance_type)); 1006 } 1007 1008 Node* CodeStubAssembler::DoesntHaveInstanceType(Node* object, 1009 InstanceType instance_type) { 1010 return Word32NotEqual(LoadInstanceType(object), Int32Constant(instance_type)); 1011 } 1012 1013 Node* CodeStubAssembler::LoadProperties(Node* object) { 1014 return LoadObjectField(object, JSObject::kPropertiesOffset); 1015 } 1016 1017 Node* CodeStubAssembler::LoadElements(Node* object) { 1018 return LoadObjectField(object, JSObject::kElementsOffset); 1019 } 1020 1021 Node* CodeStubAssembler::LoadJSArrayLength(Node* array) { 1022 CSA_ASSERT(this, IsJSArray(array)); 1023 return LoadObjectField(array, JSArray::kLengthOffset); 1024 } 1025 1026 Node* CodeStubAssembler::LoadFixedArrayBaseLength(Node* array) { 1027 return LoadObjectField(array, FixedArrayBase::kLengthOffset); 1028 } 1029 1030 Node* CodeStubAssembler::LoadAndUntagFixedArrayBaseLength(Node* array) { 1031 return LoadAndUntagObjectField(array, FixedArrayBase::kLengthOffset); 1032 } 1033 1034 Node* CodeStubAssembler::LoadMapBitField(Node* map) { 1035 CSA_SLOW_ASSERT(this, IsMap(map)); 1036 return LoadObjectField(map, Map::kBitFieldOffset, MachineType::Uint8()); 1037 } 1038 1039 Node* CodeStubAssembler::LoadMapBitField2(Node* map) { 1040 CSA_SLOW_ASSERT(this, IsMap(map)); 1041 return LoadObjectField(map, Map::kBitField2Offset, MachineType::Uint8()); 1042 } 1043 1044 Node* CodeStubAssembler::LoadMapBitField3(Node* map) { 1045 CSA_SLOW_ASSERT(this, IsMap(map)); 1046 return LoadObjectField(map, Map::kBitField3Offset, MachineType::Uint32()); 1047 } 1048 1049 Node* CodeStubAssembler::LoadMapInstanceType(Node* map) { 1050 return LoadObjectField(map, Map::kInstanceTypeOffset, MachineType::Uint8()); 1051 } 1052 1053 Node* CodeStubAssembler::LoadMapElementsKind(Node* map) { 1054 CSA_SLOW_ASSERT(this, IsMap(map)); 1055 Node* bit_field2 = LoadMapBitField2(map); 1056 return DecodeWord32<Map::ElementsKindBits>(bit_field2); 1057 } 1058 1059 Node* CodeStubAssembler::LoadMapDescriptors(Node* map) { 1060 CSA_SLOW_ASSERT(this, IsMap(map)); 1061 return LoadObjectField(map, Map::kDescriptorsOffset); 1062 } 1063 1064 Node* CodeStubAssembler::LoadMapPrototype(Node* map) { 1065 CSA_SLOW_ASSERT(this, IsMap(map)); 1066 return LoadObjectField(map, Map::kPrototypeOffset); 1067 } 1068 1069 Node* CodeStubAssembler::LoadMapPrototypeInfo(Node* map, 1070 Label* if_no_proto_info) { 1071 CSA_ASSERT(this, IsMap(map)); 1072 Node* prototype_info = 1073 LoadObjectField(map, Map::kTransitionsOrPrototypeInfoOffset); 1074 GotoIf(TaggedIsSmi(prototype_info), if_no_proto_info); 1075 GotoIfNot(WordEqual(LoadMap(prototype_info), 1076 LoadRoot(Heap::kPrototypeInfoMapRootIndex)), 1077 if_no_proto_info); 1078 return prototype_info; 1079 } 1080 1081 Node* CodeStubAssembler::LoadMapInstanceSize(Node* map) { 1082 CSA_SLOW_ASSERT(this, IsMap(map)); 1083 return ChangeUint32ToWord( 1084 LoadObjectField(map, Map::kInstanceSizeOffset, MachineType::Uint8())); 1085 } 1086 1087 Node* CodeStubAssembler::LoadMapInobjectProperties(Node* map) { 1088 CSA_SLOW_ASSERT(this, IsMap(map)); 1089 // See Map::GetInObjectProperties() for details. 1090 STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); 1091 CSA_ASSERT(this, 1092 Int32GreaterThanOrEqual(LoadMapInstanceType(map), 1093 Int32Constant(FIRST_JS_OBJECT_TYPE))); 1094 return ChangeUint32ToWord(LoadObjectField( 1095 map, Map::kInObjectPropertiesOrConstructorFunctionIndexOffset, 1096 MachineType::Uint8())); 1097 } 1098 1099 Node* CodeStubAssembler::LoadMapConstructorFunctionIndex(Node* map) { 1100 CSA_SLOW_ASSERT(this, IsMap(map)); 1101 // See Map::GetConstructorFunctionIndex() for details. 1102 STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE); 1103 CSA_ASSERT(this, Int32LessThanOrEqual(LoadMapInstanceType(map), 1104 Int32Constant(LAST_PRIMITIVE_TYPE))); 1105 return ChangeUint32ToWord(LoadObjectField( 1106 map, Map::kInObjectPropertiesOrConstructorFunctionIndexOffset, 1107 MachineType::Uint8())); 1108 } 1109 1110 Node* CodeStubAssembler::LoadMapConstructor(Node* map) { 1111 CSA_SLOW_ASSERT(this, IsMap(map)); 1112 Variable result(this, MachineRepresentation::kTagged, 1113 LoadObjectField(map, Map::kConstructorOrBackPointerOffset)); 1114 1115 Label done(this), loop(this, &result); 1116 Goto(&loop); 1117 Bind(&loop); 1118 { 1119 GotoIf(TaggedIsSmi(result.value()), &done); 1120 Node* is_map_type = 1121 Word32Equal(LoadInstanceType(result.value()), Int32Constant(MAP_TYPE)); 1122 GotoIfNot(is_map_type, &done); 1123 result.Bind( 1124 LoadObjectField(result.value(), Map::kConstructorOrBackPointerOffset)); 1125 Goto(&loop); 1126 } 1127 Bind(&done); 1128 return result.value(); 1129 } 1130 1131 Node* CodeStubAssembler::LoadSharedFunctionInfoSpecialField( 1132 Node* shared, int offset, ParameterMode mode) { 1133 if (Is64()) { 1134 Node* result = LoadObjectField(shared, offset, MachineType::Int32()); 1135 if (mode == SMI_PARAMETERS) { 1136 result = SmiTag(result); 1137 } else { 1138 result = ChangeUint32ToWord(result); 1139 } 1140 return result; 1141 } else { 1142 Node* result = LoadObjectField(shared, offset); 1143 if (mode != SMI_PARAMETERS) { 1144 result = SmiUntag(result); 1145 } 1146 return result; 1147 } 1148 } 1149 1150 Node* CodeStubAssembler::LoadNameHashField(Node* name) { 1151 CSA_ASSERT(this, IsName(name)); 1152 return LoadObjectField(name, Name::kHashFieldOffset, MachineType::Uint32()); 1153 } 1154 1155 Node* CodeStubAssembler::LoadNameHash(Node* name, Label* if_hash_not_computed) { 1156 Node* hash_field = LoadNameHashField(name); 1157 if (if_hash_not_computed != nullptr) { 1158 GotoIf(Word32Equal( 1159 Word32And(hash_field, Int32Constant(Name::kHashNotComputedMask)), 1160 Int32Constant(0)), 1161 if_hash_not_computed); 1162 } 1163 return Word32Shr(hash_field, Int32Constant(Name::kHashShift)); 1164 } 1165 1166 Node* CodeStubAssembler::LoadStringLength(Node* object) { 1167 CSA_ASSERT(this, IsString(object)); 1168 return LoadObjectField(object, String::kLengthOffset); 1169 } 1170 1171 Node* CodeStubAssembler::LoadJSValueValue(Node* object) { 1172 CSA_ASSERT(this, IsJSValue(object)); 1173 return LoadObjectField(object, JSValue::kValueOffset); 1174 } 1175 1176 Node* CodeStubAssembler::LoadWeakCellValueUnchecked(Node* weak_cell) { 1177 // TODO(ishell): fix callers. 1178 return LoadObjectField(weak_cell, WeakCell::kValueOffset); 1179 } 1180 1181 Node* CodeStubAssembler::LoadWeakCellValue(Node* weak_cell, Label* if_cleared) { 1182 CSA_ASSERT(this, IsWeakCell(weak_cell)); 1183 Node* value = LoadWeakCellValueUnchecked(weak_cell); 1184 if (if_cleared != nullptr) { 1185 GotoIf(WordEqual(value, IntPtrConstant(0)), if_cleared); 1186 } 1187 return value; 1188 } 1189 1190 Node* CodeStubAssembler::LoadFixedArrayElement(Node* object, Node* index_node, 1191 int additional_offset, 1192 ParameterMode parameter_mode) { 1193 int32_t header_size = 1194 FixedArray::kHeaderSize + additional_offset - kHeapObjectTag; 1195 Node* offset = ElementOffsetFromIndex(index_node, FAST_HOLEY_ELEMENTS, 1196 parameter_mode, header_size); 1197 return Load(MachineType::AnyTagged(), object, offset); 1198 } 1199 1200 Node* CodeStubAssembler::LoadFixedTypedArrayElement( 1201 Node* data_pointer, Node* index_node, ElementsKind elements_kind, 1202 ParameterMode parameter_mode) { 1203 Node* offset = 1204 ElementOffsetFromIndex(index_node, elements_kind, parameter_mode, 0); 1205 MachineType type; 1206 switch (elements_kind) { 1207 case UINT8_ELEMENTS: /* fall through */ 1208 case UINT8_CLAMPED_ELEMENTS: 1209 type = MachineType::Uint8(); 1210 break; 1211 case INT8_ELEMENTS: 1212 type = MachineType::Int8(); 1213 break; 1214 case UINT16_ELEMENTS: 1215 type = MachineType::Uint16(); 1216 break; 1217 case INT16_ELEMENTS: 1218 type = MachineType::Int16(); 1219 break; 1220 case UINT32_ELEMENTS: 1221 type = MachineType::Uint32(); 1222 break; 1223 case INT32_ELEMENTS: 1224 type = MachineType::Int32(); 1225 break; 1226 case FLOAT32_ELEMENTS: 1227 type = MachineType::Float32(); 1228 break; 1229 case FLOAT64_ELEMENTS: 1230 type = MachineType::Float64(); 1231 break; 1232 default: 1233 UNREACHABLE(); 1234 } 1235 return Load(type, data_pointer, offset); 1236 } 1237 1238 Node* CodeStubAssembler::LoadAndUntagToWord32FixedArrayElement( 1239 Node* object, Node* index_node, int additional_offset, 1240 ParameterMode parameter_mode) { 1241 int32_t header_size = 1242 FixedArray::kHeaderSize + additional_offset - kHeapObjectTag; 1243 #if V8_TARGET_LITTLE_ENDIAN 1244 if (Is64()) { 1245 header_size += kPointerSize / 2; 1246 } 1247 #endif 1248 Node* offset = ElementOffsetFromIndex(index_node, FAST_HOLEY_ELEMENTS, 1249 parameter_mode, header_size); 1250 if (Is64()) { 1251 return Load(MachineType::Int32(), object, offset); 1252 } else { 1253 return SmiToWord32(Load(MachineType::AnyTagged(), object, offset)); 1254 } 1255 } 1256 1257 Node* CodeStubAssembler::LoadFixedDoubleArrayElement( 1258 Node* object, Node* index_node, MachineType machine_type, 1259 int additional_offset, ParameterMode parameter_mode, Label* if_hole) { 1260 CSA_ASSERT(this, IsFixedDoubleArray(object)); 1261 int32_t header_size = 1262 FixedDoubleArray::kHeaderSize + additional_offset - kHeapObjectTag; 1263 Node* offset = ElementOffsetFromIndex(index_node, FAST_HOLEY_DOUBLE_ELEMENTS, 1264 parameter_mode, header_size); 1265 return LoadDoubleWithHoleCheck(object, offset, if_hole, machine_type); 1266 } 1267 1268 Node* CodeStubAssembler::LoadDoubleWithHoleCheck(Node* base, Node* offset, 1269 Label* if_hole, 1270 MachineType machine_type) { 1271 if (if_hole) { 1272 // TODO(ishell): Compare only the upper part for the hole once the 1273 // compiler is able to fold addition of already complex |offset| with 1274 // |kIeeeDoubleExponentWordOffset| into one addressing mode. 1275 if (Is64()) { 1276 Node* element = Load(MachineType::Uint64(), base, offset); 1277 GotoIf(Word64Equal(element, Int64Constant(kHoleNanInt64)), if_hole); 1278 } else { 1279 Node* element_upper = Load( 1280 MachineType::Uint32(), base, 1281 IntPtrAdd(offset, IntPtrConstant(kIeeeDoubleExponentWordOffset))); 1282 GotoIf(Word32Equal(element_upper, Int32Constant(kHoleNanUpper32)), 1283 if_hole); 1284 } 1285 } 1286 if (machine_type.IsNone()) { 1287 // This means the actual value is not needed. 1288 return nullptr; 1289 } 1290 return Load(machine_type, base, offset); 1291 } 1292 1293 Node* CodeStubAssembler::LoadContextElement(Node* context, int slot_index) { 1294 int offset = Context::SlotOffset(slot_index); 1295 return Load(MachineType::AnyTagged(), context, IntPtrConstant(offset)); 1296 } 1297 1298 Node* CodeStubAssembler::LoadContextElement(Node* context, Node* slot_index) { 1299 Node* offset = 1300 IntPtrAdd(WordShl(slot_index, kPointerSizeLog2), 1301 IntPtrConstant(Context::kHeaderSize - kHeapObjectTag)); 1302 return Load(MachineType::AnyTagged(), context, offset); 1303 } 1304 1305 Node* CodeStubAssembler::StoreContextElement(Node* context, int slot_index, 1306 Node* value) { 1307 int offset = Context::SlotOffset(slot_index); 1308 return Store(context, IntPtrConstant(offset), value); 1309 } 1310 1311 Node* CodeStubAssembler::StoreContextElement(Node* context, Node* slot_index, 1312 Node* value) { 1313 Node* offset = 1314 IntPtrAdd(WordShl(slot_index, kPointerSizeLog2), 1315 IntPtrConstant(Context::kHeaderSize - kHeapObjectTag)); 1316 return Store(context, offset, value); 1317 } 1318 1319 Node* CodeStubAssembler::StoreContextElementNoWriteBarrier(Node* context, 1320 int slot_index, 1321 Node* value) { 1322 int offset = Context::SlotOffset(slot_index); 1323 return StoreNoWriteBarrier(MachineRepresentation::kTagged, context, 1324 IntPtrConstant(offset), value); 1325 } 1326 1327 Node* CodeStubAssembler::LoadNativeContext(Node* context) { 1328 return LoadContextElement(context, Context::NATIVE_CONTEXT_INDEX); 1329 } 1330 1331 Node* CodeStubAssembler::LoadJSArrayElementsMap(ElementsKind kind, 1332 Node* native_context) { 1333 CSA_ASSERT(this, IsNativeContext(native_context)); 1334 return LoadContextElement(native_context, Context::ArrayMapIndex(kind)); 1335 } 1336 1337 Node* CodeStubAssembler::StoreHeapNumberValue(Node* object, Node* value) { 1338 return StoreObjectFieldNoWriteBarrier(object, HeapNumber::kValueOffset, value, 1339 MachineRepresentation::kFloat64); 1340 } 1341 1342 Node* CodeStubAssembler::StoreObjectField( 1343 Node* object, int offset, Node* value) { 1344 DCHECK_NE(HeapObject::kMapOffset, offset); // Use StoreMap instead. 1345 return Store(object, IntPtrConstant(offset - kHeapObjectTag), value); 1346 } 1347 1348 Node* CodeStubAssembler::StoreObjectField(Node* object, Node* offset, 1349 Node* value) { 1350 int const_offset; 1351 if (ToInt32Constant(offset, const_offset)) { 1352 return StoreObjectField(object, const_offset, value); 1353 } 1354 return Store(object, IntPtrSub(offset, IntPtrConstant(kHeapObjectTag)), 1355 value); 1356 } 1357 1358 Node* CodeStubAssembler::StoreObjectFieldNoWriteBarrier( 1359 Node* object, int offset, Node* value, MachineRepresentation rep) { 1360 return StoreNoWriteBarrier(rep, object, 1361 IntPtrConstant(offset - kHeapObjectTag), value); 1362 } 1363 1364 Node* CodeStubAssembler::StoreObjectFieldNoWriteBarrier( 1365 Node* object, Node* offset, Node* value, MachineRepresentation rep) { 1366 int const_offset; 1367 if (ToInt32Constant(offset, const_offset)) { 1368 return StoreObjectFieldNoWriteBarrier(object, const_offset, value, rep); 1369 } 1370 return StoreNoWriteBarrier( 1371 rep, object, IntPtrSub(offset, IntPtrConstant(kHeapObjectTag)), value); 1372 } 1373 1374 Node* CodeStubAssembler::StoreMap(Node* object, Node* map) { 1375 CSA_SLOW_ASSERT(this, IsMap(map)); 1376 return StoreWithMapWriteBarrier( 1377 object, IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag), map); 1378 } 1379 1380 Node* CodeStubAssembler::StoreMapNoWriteBarrier( 1381 Node* object, Heap::RootListIndex map_root_index) { 1382 return StoreMapNoWriteBarrier(object, LoadRoot(map_root_index)); 1383 } 1384 1385 Node* CodeStubAssembler::StoreMapNoWriteBarrier(Node* object, Node* map) { 1386 CSA_SLOW_ASSERT(this, IsMap(map)); 1387 return StoreNoWriteBarrier( 1388 MachineRepresentation::kTagged, object, 1389 IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag), map); 1390 } 1391 1392 Node* CodeStubAssembler::StoreObjectFieldRoot(Node* object, int offset, 1393 Heap::RootListIndex root_index) { 1394 if (Heap::RootIsImmortalImmovable(root_index)) { 1395 return StoreObjectFieldNoWriteBarrier(object, offset, LoadRoot(root_index)); 1396 } else { 1397 return StoreObjectField(object, offset, LoadRoot(root_index)); 1398 } 1399 } 1400 1401 Node* CodeStubAssembler::StoreFixedArrayElement(Node* object, Node* index_node, 1402 Node* value, 1403 WriteBarrierMode barrier_mode, 1404 int additional_offset, 1405 ParameterMode parameter_mode) { 1406 DCHECK(barrier_mode == SKIP_WRITE_BARRIER || 1407 barrier_mode == UPDATE_WRITE_BARRIER); 1408 int header_size = 1409 FixedArray::kHeaderSize + additional_offset - kHeapObjectTag; 1410 Node* offset = ElementOffsetFromIndex(index_node, FAST_HOLEY_ELEMENTS, 1411 parameter_mode, header_size); 1412 if (barrier_mode == SKIP_WRITE_BARRIER) { 1413 return StoreNoWriteBarrier(MachineRepresentation::kTagged, object, offset, 1414 value); 1415 } else { 1416 return Store(object, offset, value); 1417 } 1418 } 1419 1420 Node* CodeStubAssembler::StoreFixedDoubleArrayElement( 1421 Node* object, Node* index_node, Node* value, ParameterMode parameter_mode) { 1422 CSA_ASSERT(this, IsFixedDoubleArray(object)); 1423 Node* offset = 1424 ElementOffsetFromIndex(index_node, FAST_DOUBLE_ELEMENTS, parameter_mode, 1425 FixedArray::kHeaderSize - kHeapObjectTag); 1426 MachineRepresentation rep = MachineRepresentation::kFloat64; 1427 return StoreNoWriteBarrier(rep, object, offset, value); 1428 } 1429 1430 Node* CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* context, 1431 Node* array, 1432 CodeStubArguments& args, 1433 Variable& arg_index, 1434 Label* bailout) { 1435 Comment("BuildAppendJSArray: %s", ElementsKindToString(kind)); 1436 Label pre_bailout(this); 1437 Label success(this); 1438 Variable var_tagged_length(this, MachineRepresentation::kTagged); 1439 ParameterMode mode = OptimalParameterMode(); 1440 Variable var_length(this, OptimalParameterRepresentation(), 1441 TaggedToParameter(LoadJSArrayLength(array), mode)); 1442 Variable var_elements(this, MachineRepresentation::kTagged, 1443 LoadElements(array)); 1444 Node* capacity = 1445 TaggedToParameter(LoadFixedArrayBaseLength(var_elements.value()), mode); 1446 1447 // Resize the capacity of the fixed array if it doesn't fit. 1448 Label fits(this, &var_elements); 1449 Node* first = arg_index.value(); 1450 Node* growth = IntPtrSub(args.GetLength(), first); 1451 Node* new_length = 1452 IntPtrOrSmiAdd(WordToParameter(growth, mode), var_length.value(), mode); 1453 GotoIfNot(IntPtrOrSmiGreaterThan(new_length, capacity, mode), &fits); 1454 Node* new_capacity = CalculateNewElementsCapacity(new_length, mode); 1455 var_elements.Bind(GrowElementsCapacity(array, var_elements.value(), kind, 1456 kind, capacity, new_capacity, mode, 1457 &pre_bailout)); 1458 Goto(&fits); 1459 Bind(&fits); 1460 Node* elements = var_elements.value(); 1461 1462 // Push each argument onto the end of the array now that there is enough 1463 // capacity. 1464 CodeStubAssembler::VariableList push_vars({&var_length}, zone()); 1465 args.ForEach( 1466 push_vars, 1467 [this, kind, mode, elements, &var_length, &pre_bailout](Node* arg) { 1468 if (IsFastSmiElementsKind(kind)) { 1469 GotoIf(TaggedIsNotSmi(arg), &pre_bailout); 1470 } else if (IsFastDoubleElementsKind(kind)) { 1471 GotoIfNotNumber(arg, &pre_bailout); 1472 } 1473 if (IsFastDoubleElementsKind(kind)) { 1474 Node* double_value = ChangeNumberToFloat64(arg); 1475 StoreFixedDoubleArrayElement(elements, var_length.value(), 1476 Float64SilenceNaN(double_value), mode); 1477 } else { 1478 WriteBarrierMode barrier_mode = IsFastSmiElementsKind(kind) 1479 ? SKIP_WRITE_BARRIER 1480 : UPDATE_WRITE_BARRIER; 1481 StoreFixedArrayElement(elements, var_length.value(), arg, 1482 barrier_mode, 0, mode); 1483 } 1484 Increment(var_length, 1, mode); 1485 }, 1486 first, nullptr); 1487 { 1488 Node* length = ParameterToTagged(var_length.value(), mode); 1489 var_tagged_length.Bind(length); 1490 StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length); 1491 Goto(&success); 1492 } 1493 1494 Bind(&pre_bailout); 1495 { 1496 Node* length = ParameterToTagged(var_length.value(), mode); 1497 var_tagged_length.Bind(length); 1498 Node* diff = SmiSub(length, LoadJSArrayLength(array)); 1499 StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length); 1500 arg_index.Bind(IntPtrAdd(arg_index.value(), SmiUntag(diff))); 1501 Goto(bailout); 1502 } 1503 1504 Bind(&success); 1505 return var_tagged_length.value(); 1506 } 1507 1508 Node* CodeStubAssembler::AllocateHeapNumber(MutableMode mode) { 1509 Node* result = Allocate(HeapNumber::kSize, kNone); 1510 Heap::RootListIndex heap_map_index = 1511 mode == IMMUTABLE ? Heap::kHeapNumberMapRootIndex 1512 : Heap::kMutableHeapNumberMapRootIndex; 1513 StoreMapNoWriteBarrier(result, heap_map_index); 1514 return result; 1515 } 1516 1517 Node* CodeStubAssembler::AllocateHeapNumberWithValue(Node* value, 1518 MutableMode mode) { 1519 Node* result = AllocateHeapNumber(mode); 1520 StoreHeapNumberValue(result, value); 1521 return result; 1522 } 1523 1524 Node* CodeStubAssembler::AllocateSeqOneByteString(int length, 1525 AllocationFlags flags) { 1526 Comment("AllocateSeqOneByteString"); 1527 if (length == 0) { 1528 return LoadRoot(Heap::kempty_stringRootIndex); 1529 } 1530 Node* result = Allocate(SeqOneByteString::SizeFor(length), flags); 1531 DCHECK(Heap::RootIsImmortalImmovable(Heap::kOneByteStringMapRootIndex)); 1532 StoreMapNoWriteBarrier(result, Heap::kOneByteStringMapRootIndex); 1533 StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kLengthOffset, 1534 SmiConstant(Smi::FromInt(length))); 1535 // Initialize both used and unused parts of hash field slot at once. 1536 StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kHashFieldSlot, 1537 IntPtrConstant(String::kEmptyHashField), 1538 MachineType::PointerRepresentation()); 1539 return result; 1540 } 1541 1542 Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length, 1543 ParameterMode mode, 1544 AllocationFlags flags) { 1545 Comment("AllocateSeqOneByteString"); 1546 Variable var_result(this, MachineRepresentation::kTagged); 1547 1548 // Compute the SeqOneByteString size and check if it fits into new space. 1549 Label if_lengthiszero(this), if_sizeissmall(this), 1550 if_notsizeissmall(this, Label::kDeferred), if_join(this); 1551 GotoIf(WordEqual(length, IntPtrOrSmiConstant(0, mode)), &if_lengthiszero); 1552 1553 Node* raw_size = GetArrayAllocationSize( 1554 length, UINT8_ELEMENTS, mode, 1555 SeqOneByteString::kHeaderSize + kObjectAlignmentMask); 1556 Node* size = WordAnd(raw_size, IntPtrConstant(~kObjectAlignmentMask)); 1557 Branch(IntPtrLessThanOrEqual(size, IntPtrConstant(kMaxRegularHeapObjectSize)), 1558 &if_sizeissmall, &if_notsizeissmall); 1559 1560 Bind(&if_sizeissmall); 1561 { 1562 // Just allocate the SeqOneByteString in new space. 1563 Node* result = Allocate(size, flags); 1564 DCHECK(Heap::RootIsImmortalImmovable(Heap::kOneByteStringMapRootIndex)); 1565 StoreMapNoWriteBarrier(result, Heap::kOneByteStringMapRootIndex); 1566 StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kLengthOffset, 1567 ParameterToTagged(length, mode)); 1568 // Initialize both used and unused parts of hash field slot at once. 1569 StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kHashFieldSlot, 1570 IntPtrConstant(String::kEmptyHashField), 1571 MachineType::PointerRepresentation()); 1572 var_result.Bind(result); 1573 Goto(&if_join); 1574 } 1575 1576 Bind(&if_notsizeissmall); 1577 { 1578 // We might need to allocate in large object space, go to the runtime. 1579 Node* result = CallRuntime(Runtime::kAllocateSeqOneByteString, context, 1580 ParameterToTagged(length, mode)); 1581 var_result.Bind(result); 1582 Goto(&if_join); 1583 } 1584 1585 Bind(&if_lengthiszero); 1586 { 1587 var_result.Bind(LoadRoot(Heap::kempty_stringRootIndex)); 1588 Goto(&if_join); 1589 } 1590 1591 Bind(&if_join); 1592 return var_result.value(); 1593 } 1594 1595 Node* CodeStubAssembler::AllocateSeqTwoByteString(int length, 1596 AllocationFlags flags) { 1597 Comment("AllocateSeqTwoByteString"); 1598 if (length == 0) { 1599 return LoadRoot(Heap::kempty_stringRootIndex); 1600 } 1601 Node* result = Allocate(SeqTwoByteString::SizeFor(length), flags); 1602 DCHECK(Heap::RootIsImmortalImmovable(Heap::kStringMapRootIndex)); 1603 StoreMapNoWriteBarrier(result, Heap::kStringMapRootIndex); 1604 StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kLengthOffset, 1605 SmiConstant(Smi::FromInt(length))); 1606 // Initialize both used and unused parts of hash field slot at once. 1607 StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kHashFieldSlot, 1608 IntPtrConstant(String::kEmptyHashField), 1609 MachineType::PointerRepresentation()); 1610 return result; 1611 } 1612 1613 Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length, 1614 ParameterMode mode, 1615 AllocationFlags flags) { 1616 Comment("AllocateSeqTwoByteString"); 1617 Variable var_result(this, MachineRepresentation::kTagged); 1618 1619 // Compute the SeqTwoByteString size and check if it fits into new space. 1620 Label if_lengthiszero(this), if_sizeissmall(this), 1621 if_notsizeissmall(this, Label::kDeferred), if_join(this); 1622 GotoIf(WordEqual(length, IntPtrOrSmiConstant(0, mode)), &if_lengthiszero); 1623 1624 Node* raw_size = GetArrayAllocationSize( 1625 length, UINT16_ELEMENTS, mode, 1626 SeqOneByteString::kHeaderSize + kObjectAlignmentMask); 1627 Node* size = WordAnd(raw_size, IntPtrConstant(~kObjectAlignmentMask)); 1628 Branch(IntPtrLessThanOrEqual(size, IntPtrConstant(kMaxRegularHeapObjectSize)), 1629 &if_sizeissmall, &if_notsizeissmall); 1630 1631 Bind(&if_sizeissmall); 1632 { 1633 // Just allocate the SeqTwoByteString in new space. 1634 Node* result = Allocate(size, flags); 1635 DCHECK(Heap::RootIsImmortalImmovable(Heap::kStringMapRootIndex)); 1636 StoreMapNoWriteBarrier(result, Heap::kStringMapRootIndex); 1637 StoreObjectFieldNoWriteBarrier( 1638 result, SeqTwoByteString::kLengthOffset, 1639 mode == SMI_PARAMETERS ? length : SmiFromWord(length)); 1640 // Initialize both used and unused parts of hash field slot at once. 1641 StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kHashFieldSlot, 1642 IntPtrConstant(String::kEmptyHashField), 1643 MachineType::PointerRepresentation()); 1644 var_result.Bind(result); 1645 Goto(&if_join); 1646 } 1647 1648 Bind(&if_notsizeissmall); 1649 { 1650 // We might need to allocate in large object space, go to the runtime. 1651 Node* result = 1652 CallRuntime(Runtime::kAllocateSeqTwoByteString, context, 1653 mode == SMI_PARAMETERS ? length : SmiFromWord(length)); 1654 var_result.Bind(result); 1655 Goto(&if_join); 1656 } 1657 1658 Bind(&if_lengthiszero); 1659 { 1660 var_result.Bind(LoadRoot(Heap::kempty_stringRootIndex)); 1661 Goto(&if_join); 1662 } 1663 1664 Bind(&if_join); 1665 return var_result.value(); 1666 } 1667 1668 Node* CodeStubAssembler::AllocateSlicedString( 1669 Heap::RootListIndex map_root_index, Node* length, Node* parent, 1670 Node* offset) { 1671 CSA_ASSERT(this, TaggedIsSmi(length)); 1672 Node* result = Allocate(SlicedString::kSize); 1673 DCHECK(Heap::RootIsImmortalImmovable(map_root_index)); 1674 StoreMapNoWriteBarrier(result, map_root_index); 1675 StoreObjectFieldNoWriteBarrier(result, SlicedString::kLengthOffset, length, 1676 MachineRepresentation::kTagged); 1677 // Initialize both used and unused parts of hash field slot at once. 1678 StoreObjectFieldNoWriteBarrier(result, SlicedString::kHashFieldSlot, 1679 IntPtrConstant(String::kEmptyHashField), 1680 MachineType::PointerRepresentation()); 1681 StoreObjectFieldNoWriteBarrier(result, SlicedString::kParentOffset, parent, 1682 MachineRepresentation::kTagged); 1683 StoreObjectFieldNoWriteBarrier(result, SlicedString::kOffsetOffset, offset, 1684 MachineRepresentation::kTagged); 1685 return result; 1686 } 1687 1688 Node* CodeStubAssembler::AllocateSlicedOneByteString(Node* length, Node* parent, 1689 Node* offset) { 1690 return AllocateSlicedString(Heap::kSlicedOneByteStringMapRootIndex, length, 1691 parent, offset); 1692 } 1693 1694 Node* CodeStubAssembler::AllocateSlicedTwoByteString(Node* length, Node* parent, 1695 Node* offset) { 1696 return AllocateSlicedString(Heap::kSlicedStringMapRootIndex, length, parent, 1697 offset); 1698 } 1699 1700 Node* CodeStubAssembler::AllocateConsString(Heap::RootListIndex map_root_index, 1701 Node* length, Node* first, 1702 Node* second, 1703 AllocationFlags flags) { 1704 CSA_ASSERT(this, TaggedIsSmi(length)); 1705 Node* result = Allocate(ConsString::kSize, flags); 1706 DCHECK(Heap::RootIsImmortalImmovable(map_root_index)); 1707 StoreMapNoWriteBarrier(result, map_root_index); 1708 StoreObjectFieldNoWriteBarrier(result, ConsString::kLengthOffset, length, 1709 MachineRepresentation::kTagged); 1710 // Initialize both used and unused parts of hash field slot at once. 1711 StoreObjectFieldNoWriteBarrier(result, ConsString::kHashFieldSlot, 1712 IntPtrConstant(String::kEmptyHashField), 1713 MachineType::PointerRepresentation()); 1714 bool const new_space = !(flags & kPretenured); 1715 if (new_space) { 1716 StoreObjectFieldNoWriteBarrier(result, ConsString::kFirstOffset, first, 1717 MachineRepresentation::kTagged); 1718 StoreObjectFieldNoWriteBarrier(result, ConsString::kSecondOffset, second, 1719 MachineRepresentation::kTagged); 1720 } else { 1721 StoreObjectField(result, ConsString::kFirstOffset, first); 1722 StoreObjectField(result, ConsString::kSecondOffset, second); 1723 } 1724 return result; 1725 } 1726 1727 Node* CodeStubAssembler::AllocateOneByteConsString(Node* length, Node* first, 1728 Node* second, 1729 AllocationFlags flags) { 1730 return AllocateConsString(Heap::kConsOneByteStringMapRootIndex, length, first, 1731 second, flags); 1732 } 1733 1734 Node* CodeStubAssembler::AllocateTwoByteConsString(Node* length, Node* first, 1735 Node* second, 1736 AllocationFlags flags) { 1737 return AllocateConsString(Heap::kConsStringMapRootIndex, length, first, 1738 second, flags); 1739 } 1740 1741 Node* CodeStubAssembler::NewConsString(Node* context, Node* length, Node* left, 1742 Node* right, AllocationFlags flags) { 1743 CSA_ASSERT(this, TaggedIsSmi(length)); 1744 // Added string can be a cons string. 1745 Comment("Allocating ConsString"); 1746 Node* left_instance_type = LoadInstanceType(left); 1747 Node* right_instance_type = LoadInstanceType(right); 1748 1749 // Compute intersection and difference of instance types. 1750 Node* anded_instance_types = 1751 Word32And(left_instance_type, right_instance_type); 1752 Node* xored_instance_types = 1753 Word32Xor(left_instance_type, right_instance_type); 1754 1755 // We create a one-byte cons string if 1756 // 1. both strings are one-byte, or 1757 // 2. at least one of the strings is two-byte, but happens to contain only 1758 // one-byte characters. 1759 // To do this, we check 1760 // 1. if both strings are one-byte, or if the one-byte data hint is set in 1761 // both strings, or 1762 // 2. if one of the strings has the one-byte data hint set and the other 1763 // string is one-byte. 1764 STATIC_ASSERT(kOneByteStringTag != 0); 1765 STATIC_ASSERT(kOneByteDataHintTag != 0); 1766 Label one_byte_map(this); 1767 Label two_byte_map(this); 1768 Variable result(this, MachineRepresentation::kTagged); 1769 Label done(this, &result); 1770 GotoIf(Word32NotEqual(Word32And(anded_instance_types, 1771 Int32Constant(kStringEncodingMask | 1772 kOneByteDataHintTag)), 1773 Int32Constant(0)), 1774 &one_byte_map); 1775 Branch(Word32NotEqual(Word32And(xored_instance_types, 1776 Int32Constant(kStringEncodingMask | 1777 kOneByteDataHintMask)), 1778 Int32Constant(kOneByteStringTag | kOneByteDataHintTag)), 1779 &two_byte_map, &one_byte_map); 1780 1781 Bind(&one_byte_map); 1782 Comment("One-byte ConsString"); 1783 result.Bind(AllocateOneByteConsString(length, left, right, flags)); 1784 Goto(&done); 1785 1786 Bind(&two_byte_map); 1787 Comment("Two-byte ConsString"); 1788 result.Bind(AllocateTwoByteConsString(length, left, right, flags)); 1789 Goto(&done); 1790 1791 Bind(&done); 1792 1793 return result.value(); 1794 } 1795 1796 Node* CodeStubAssembler::AllocateRegExpResult(Node* context, Node* length, 1797 Node* index, Node* input) { 1798 Node* const max_length = 1799 SmiConstant(Smi::FromInt(JSArray::kInitialMaxFastElementArray)); 1800 CSA_ASSERT(this, SmiLessThanOrEqual(length, max_length)); 1801 USE(max_length); 1802 1803 // Allocate the JSRegExpResult. 1804 // TODO(jgruber): Fold JSArray and FixedArray allocations, then remove 1805 // unneeded store of elements. 1806 Node* const result = Allocate(JSRegExpResult::kSize); 1807 1808 // TODO(jgruber): Store map as Heap constant? 1809 Node* const native_context = LoadNativeContext(context); 1810 Node* const map = 1811 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); 1812 StoreMapNoWriteBarrier(result, map); 1813 1814 // Initialize the header before allocating the elements. 1815 Node* const empty_array = EmptyFixedArrayConstant(); 1816 DCHECK(Heap::RootIsImmortalImmovable(Heap::kEmptyFixedArrayRootIndex)); 1817 StoreObjectFieldNoWriteBarrier(result, JSArray::kPropertiesOffset, 1818 empty_array); 1819 StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset, empty_array); 1820 StoreObjectFieldNoWriteBarrier(result, JSArray::kLengthOffset, length); 1821 1822 StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kIndexOffset, index); 1823 StoreObjectField(result, JSRegExpResult::kInputOffset, input); 1824 1825 Node* const zero = IntPtrConstant(0); 1826 Node* const length_intptr = SmiUntag(length); 1827 const ElementsKind elements_kind = FAST_ELEMENTS; 1828 1829 Node* const elements = AllocateFixedArray(elements_kind, length_intptr); 1830 StoreObjectField(result, JSArray::kElementsOffset, elements); 1831 1832 // Fill in the elements with undefined. 1833 FillFixedArrayWithValue(elements_kind, elements, zero, length_intptr, 1834 Heap::kUndefinedValueRootIndex); 1835 1836 return result; 1837 } 1838 1839 Node* CodeStubAssembler::AllocateNameDictionary(int at_least_space_for) { 1840 return AllocateNameDictionary(IntPtrConstant(at_least_space_for)); 1841 } 1842 1843 Node* CodeStubAssembler::AllocateNameDictionary(Node* at_least_space_for) { 1844 CSA_ASSERT(this, UintPtrLessThanOrEqual( 1845 at_least_space_for, 1846 IntPtrConstant(NameDictionary::kMaxCapacity))); 1847 1848 Node* capacity = HashTableComputeCapacity(at_least_space_for); 1849 CSA_ASSERT(this, WordIsPowerOfTwo(capacity)); 1850 1851 Node* length = EntryToIndex<NameDictionary>(capacity); 1852 Node* store_size = 1853 IntPtrAdd(WordShl(length, IntPtrConstant(kPointerSizeLog2)), 1854 IntPtrConstant(NameDictionary::kHeaderSize)); 1855 1856 Node* result = Allocate(store_size); 1857 Comment("Initialize NameDictionary"); 1858 // Initialize FixedArray fields. 1859 DCHECK(Heap::RootIsImmortalImmovable(Heap::kHashTableMapRootIndex)); 1860 StoreMapNoWriteBarrier(result, Heap::kHashTableMapRootIndex); 1861 StoreObjectFieldNoWriteBarrier(result, FixedArray::kLengthOffset, 1862 SmiFromWord(length)); 1863 // Initialized HashTable fields. 1864 Node* zero = SmiConstant(0); 1865 StoreFixedArrayElement(result, NameDictionary::kNumberOfElementsIndex, zero, 1866 SKIP_WRITE_BARRIER); 1867 StoreFixedArrayElement(result, NameDictionary::kNumberOfDeletedElementsIndex, 1868 zero, SKIP_WRITE_BARRIER); 1869 StoreFixedArrayElement(result, NameDictionary::kCapacityIndex, 1870 SmiTag(capacity), SKIP_WRITE_BARRIER); 1871 // Initialize Dictionary fields. 1872 Node* filler = LoadRoot(Heap::kUndefinedValueRootIndex); 1873 StoreFixedArrayElement(result, NameDictionary::kMaxNumberKeyIndex, filler, 1874 SKIP_WRITE_BARRIER); 1875 StoreFixedArrayElement(result, NameDictionary::kNextEnumerationIndexIndex, 1876 SmiConstant(PropertyDetails::kInitialIndex), 1877 SKIP_WRITE_BARRIER); 1878 1879 // Initialize NameDictionary elements. 1880 Node* result_word = BitcastTaggedToWord(result); 1881 Node* start_address = IntPtrAdd( 1882 result_word, IntPtrConstant(NameDictionary::OffsetOfElementAt( 1883 NameDictionary::kElementsStartIndex) - 1884 kHeapObjectTag)); 1885 Node* end_address = IntPtrAdd( 1886 result_word, IntPtrSub(store_size, IntPtrConstant(kHeapObjectTag))); 1887 StoreFieldsNoWriteBarrier(start_address, end_address, filler); 1888 return result; 1889 } 1890 1891 Node* CodeStubAssembler::AllocateJSObjectFromMap(Node* map, Node* properties, 1892 Node* elements, 1893 AllocationFlags flags) { 1894 CSA_ASSERT(this, IsMap(map)); 1895 Node* size = 1896 IntPtrMul(LoadMapInstanceSize(map), IntPtrConstant(kPointerSize)); 1897 CSA_ASSERT(this, IsRegularHeapObjectSize(size)); 1898 Node* object = Allocate(size, flags); 1899 StoreMapNoWriteBarrier(object, map); 1900 InitializeJSObjectFromMap(object, map, size, properties, elements); 1901 return object; 1902 } 1903 1904 void CodeStubAssembler::InitializeJSObjectFromMap(Node* object, Node* map, 1905 Node* size, Node* properties, 1906 Node* elements) { 1907 // This helper assumes that the object is in new-space, as guarded by the 1908 // check in AllocatedJSObjectFromMap. 1909 if (properties == nullptr) { 1910 CSA_ASSERT(this, Word32BinaryNot(IsDictionaryMap((map)))); 1911 StoreObjectFieldRoot(object, JSObject::kPropertiesOffset, 1912 Heap::kEmptyFixedArrayRootIndex); 1913 } else { 1914 StoreObjectFieldNoWriteBarrier(object, JSObject::kPropertiesOffset, 1915 properties); 1916 } 1917 if (elements == nullptr) { 1918 StoreObjectFieldRoot(object, JSObject::kElementsOffset, 1919 Heap::kEmptyFixedArrayRootIndex); 1920 } else { 1921 StoreObjectFieldNoWriteBarrier(object, JSObject::kElementsOffset, elements); 1922 } 1923 InitializeJSObjectBody(object, map, size, JSObject::kHeaderSize); 1924 } 1925 1926 void CodeStubAssembler::InitializeJSObjectBody(Node* object, Node* map, 1927 Node* size, int start_offset) { 1928 // TODO(cbruni): activate in-object slack tracking machinery. 1929 Comment("InitializeJSObjectBody"); 1930 Node* filler = LoadRoot(Heap::kUndefinedValueRootIndex); 1931 // Calculate the untagged field addresses. 1932 object = BitcastTaggedToWord(object); 1933 Node* start_address = 1934 IntPtrAdd(object, IntPtrConstant(start_offset - kHeapObjectTag)); 1935 Node* end_address = 1936 IntPtrSub(IntPtrAdd(object, size), IntPtrConstant(kHeapObjectTag)); 1937 StoreFieldsNoWriteBarrier(start_address, end_address, filler); 1938 } 1939 1940 void CodeStubAssembler::StoreFieldsNoWriteBarrier(Node* start_address, 1941 Node* end_address, 1942 Node* value) { 1943 Comment("StoreFieldsNoWriteBarrier"); 1944 CSA_ASSERT(this, WordIsWordAligned(start_address)); 1945 CSA_ASSERT(this, WordIsWordAligned(end_address)); 1946 BuildFastLoop(start_address, end_address, 1947 [this, value](Node* current) { 1948 StoreNoWriteBarrier(MachineRepresentation::kTagged, current, 1949 value); 1950 }, 1951 kPointerSize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost); 1952 } 1953 1954 Node* CodeStubAssembler::AllocateUninitializedJSArrayWithoutElements( 1955 ElementsKind kind, Node* array_map, Node* length, Node* allocation_site) { 1956 Comment("begin allocation of JSArray without elements"); 1957 int base_size = JSArray::kSize; 1958 if (allocation_site != nullptr) { 1959 base_size += AllocationMemento::kSize; 1960 } 1961 1962 Node* size = IntPtrConstant(base_size); 1963 Node* array = AllocateUninitializedJSArray(kind, array_map, length, 1964 allocation_site, size); 1965 return array; 1966 } 1967 1968 std::pair<Node*, Node*> 1969 CodeStubAssembler::AllocateUninitializedJSArrayWithElements( 1970 ElementsKind kind, Node* array_map, Node* length, Node* allocation_site, 1971 Node* capacity, ParameterMode capacity_mode) { 1972 Comment("begin allocation of JSArray with elements"); 1973 int base_size = JSArray::kSize; 1974 1975 if (allocation_site != nullptr) { 1976 base_size += AllocationMemento::kSize; 1977 } 1978 1979 int elements_offset = base_size; 1980 1981 // Compute space for elements 1982 base_size += FixedArray::kHeaderSize; 1983 Node* size = ElementOffsetFromIndex(capacity, kind, capacity_mode, base_size); 1984 1985 Node* array = AllocateUninitializedJSArray(kind, array_map, length, 1986 allocation_site, size); 1987 1988 Node* elements = InnerAllocate(array, elements_offset); 1989 StoreObjectFieldNoWriteBarrier(array, JSObject::kElementsOffset, elements); 1990 1991 return {array, elements}; 1992 } 1993 1994 Node* CodeStubAssembler::AllocateUninitializedJSArray(ElementsKind kind, 1995 Node* array_map, 1996 Node* length, 1997 Node* allocation_site, 1998 Node* size_in_bytes) { 1999 Node* array = Allocate(size_in_bytes); 2000 2001 Comment("write JSArray headers"); 2002 StoreMapNoWriteBarrier(array, array_map); 2003 2004 CSA_ASSERT(this, TaggedIsSmi(length)); 2005 StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length); 2006 2007 StoreObjectFieldRoot(array, JSArray::kPropertiesOffset, 2008 Heap::kEmptyFixedArrayRootIndex); 2009 2010 if (allocation_site != nullptr) { 2011 InitializeAllocationMemento(array, JSArray::kSize, allocation_site); 2012 } 2013 return array; 2014 } 2015 2016 Node* CodeStubAssembler::AllocateJSArray(ElementsKind kind, Node* array_map, 2017 Node* capacity, Node* length, 2018 Node* allocation_site, 2019 ParameterMode capacity_mode) { 2020 Node *array = nullptr, *elements = nullptr; 2021 if (IsIntPtrOrSmiConstantZero(capacity)) { 2022 // Array is empty. Use the shared empty fixed array instead of allocating a 2023 // new one. 2024 array = AllocateUninitializedJSArrayWithoutElements(kind, array_map, length, 2025 nullptr); 2026 StoreObjectFieldRoot(array, JSArray::kElementsOffset, 2027 Heap::kEmptyFixedArrayRootIndex); 2028 } else { 2029 // Allocate both array and elements object, and initialize the JSArray. 2030 std::tie(array, elements) = AllocateUninitializedJSArrayWithElements( 2031 kind, array_map, length, allocation_site, capacity, capacity_mode); 2032 // Setup elements object. 2033 Heap::RootListIndex elements_map_index = 2034 IsFastDoubleElementsKind(kind) ? Heap::kFixedDoubleArrayMapRootIndex 2035 : Heap::kFixedArrayMapRootIndex; 2036 DCHECK(Heap::RootIsImmortalImmovable(elements_map_index)); 2037 StoreMapNoWriteBarrier(elements, elements_map_index); 2038 StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, 2039 ParameterToTagged(capacity, capacity_mode)); 2040 // Fill in the elements with holes. 2041 FillFixedArrayWithValue(kind, elements, 2042 IntPtrOrSmiConstant(0, capacity_mode), capacity, 2043 Heap::kTheHoleValueRootIndex, capacity_mode); 2044 } 2045 2046 return array; 2047 } 2048 2049 Node* CodeStubAssembler::AllocateFixedArray(ElementsKind kind, 2050 Node* capacity_node, 2051 ParameterMode mode, 2052 AllocationFlags flags) { 2053 CSA_ASSERT(this, IntPtrOrSmiGreaterThan(capacity_node, 2054 IntPtrOrSmiConstant(0, mode), mode)); 2055 Node* total_size = GetFixedArrayAllocationSize(capacity_node, kind, mode); 2056 2057 // Allocate both array and elements object, and initialize the JSArray. 2058 Node* array = Allocate(total_size, flags); 2059 Heap::RootListIndex map_index = IsFastDoubleElementsKind(kind) 2060 ? Heap::kFixedDoubleArrayMapRootIndex 2061 : Heap::kFixedArrayMapRootIndex; 2062 DCHECK(Heap::RootIsImmortalImmovable(map_index)); 2063 StoreMapNoWriteBarrier(array, map_index); 2064 StoreObjectFieldNoWriteBarrier(array, FixedArray::kLengthOffset, 2065 ParameterToTagged(capacity_node, mode)); 2066 return array; 2067 } 2068 2069 void CodeStubAssembler::FillFixedArrayWithValue( 2070 ElementsKind kind, Node* array, Node* from_node, Node* to_node, 2071 Heap::RootListIndex value_root_index, ParameterMode mode) { 2072 bool is_double = IsFastDoubleElementsKind(kind); 2073 DCHECK(value_root_index == Heap::kTheHoleValueRootIndex || 2074 value_root_index == Heap::kUndefinedValueRootIndex); 2075 DCHECK_IMPLIES(is_double, value_root_index == Heap::kTheHoleValueRootIndex); 2076 STATIC_ASSERT(kHoleNanLower32 == kHoleNanUpper32); 2077 Node* double_hole = 2078 Is64() ? Int64Constant(kHoleNanInt64) : Int32Constant(kHoleNanLower32); 2079 Node* value = LoadRoot(value_root_index); 2080 2081 BuildFastFixedArrayForEach( 2082 array, kind, from_node, to_node, 2083 [this, value, is_double, double_hole](Node* array, Node* offset) { 2084 if (is_double) { 2085 // Don't use doubles to store the hole double, since manipulating the 2086 // signaling NaN used for the hole in C++, e.g. with bit_cast, will 2087 // change its value on ia32 (the x87 stack is used to return values 2088 // and stores to the stack silently clear the signalling bit). 2089 // 2090 // TODO(danno): When we have a Float32/Float64 wrapper class that 2091 // preserves double bits during manipulation, remove this code/change 2092 // this to an indexed Float64 store. 2093 if (Is64()) { 2094 StoreNoWriteBarrier(MachineRepresentation::kWord64, array, offset, 2095 double_hole); 2096 } else { 2097 StoreNoWriteBarrier(MachineRepresentation::kWord32, array, offset, 2098 double_hole); 2099 StoreNoWriteBarrier(MachineRepresentation::kWord32, array, 2100 IntPtrAdd(offset, IntPtrConstant(kPointerSize)), 2101 double_hole); 2102 } 2103 } else { 2104 StoreNoWriteBarrier(MachineRepresentation::kTagged, array, offset, 2105 value); 2106 } 2107 }, 2108 mode); 2109 } 2110 2111 void CodeStubAssembler::CopyFixedArrayElements( 2112 ElementsKind from_kind, Node* from_array, ElementsKind to_kind, 2113 Node* to_array, Node* element_count, Node* capacity, 2114 WriteBarrierMode barrier_mode, ParameterMode mode) { 2115 STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize); 2116 const int first_element_offset = FixedArray::kHeaderSize - kHeapObjectTag; 2117 Comment("[ CopyFixedArrayElements"); 2118 2119 // Typed array elements are not supported. 2120 DCHECK(!IsFixedTypedArrayElementsKind(from_kind)); 2121 DCHECK(!IsFixedTypedArrayElementsKind(to_kind)); 2122 2123 Label done(this); 2124 bool from_double_elements = IsFastDoubleElementsKind(from_kind); 2125 bool to_double_elements = IsFastDoubleElementsKind(to_kind); 2126 bool element_size_matches = 2127 Is64() || 2128 IsFastDoubleElementsKind(from_kind) == IsFastDoubleElementsKind(to_kind); 2129 bool doubles_to_objects_conversion = 2130 IsFastDoubleElementsKind(from_kind) && IsFastObjectElementsKind(to_kind); 2131 bool needs_write_barrier = 2132 doubles_to_objects_conversion || (barrier_mode == UPDATE_WRITE_BARRIER && 2133 IsFastObjectElementsKind(to_kind)); 2134 Node* double_hole = 2135 Is64() ? Int64Constant(kHoleNanInt64) : Int32Constant(kHoleNanLower32); 2136 2137 if (doubles_to_objects_conversion) { 2138 // If the copy might trigger a GC, make sure that the FixedArray is 2139 // pre-initialized with holes to make sure that it's always in a 2140 // consistent state. 2141 FillFixedArrayWithValue(to_kind, to_array, IntPtrOrSmiConstant(0, mode), 2142 capacity, Heap::kTheHoleValueRootIndex, mode); 2143 } else if (element_count != capacity) { 2144 FillFixedArrayWithValue(to_kind, to_array, element_count, capacity, 2145 Heap::kTheHoleValueRootIndex, mode); 2146 } 2147 2148 Node* limit_offset = ElementOffsetFromIndex( 2149 IntPtrOrSmiConstant(0, mode), from_kind, mode, first_element_offset); 2150 Variable var_from_offset(this, MachineType::PointerRepresentation(), 2151 ElementOffsetFromIndex(element_count, from_kind, 2152 mode, first_element_offset)); 2153 // This second variable is used only when the element sizes of source and 2154 // destination arrays do not match. 2155 Variable var_to_offset(this, MachineType::PointerRepresentation()); 2156 if (element_size_matches) { 2157 var_to_offset.Bind(var_from_offset.value()); 2158 } else { 2159 var_to_offset.Bind(ElementOffsetFromIndex(element_count, to_kind, mode, 2160 first_element_offset)); 2161 } 2162 2163 Variable* vars[] = {&var_from_offset, &var_to_offset}; 2164 Label decrement(this, 2, vars); 2165 2166 Branch(WordEqual(var_from_offset.value(), limit_offset), &done, &decrement); 2167 2168 Bind(&decrement); 2169 { 2170 Node* from_offset = IntPtrSub( 2171 var_from_offset.value(), 2172 IntPtrConstant(from_double_elements ? kDoubleSize : kPointerSize)); 2173 var_from_offset.Bind(from_offset); 2174 2175 Node* to_offset; 2176 if (element_size_matches) { 2177 to_offset = from_offset; 2178 } else { 2179 to_offset = IntPtrSub( 2180 var_to_offset.value(), 2181 IntPtrConstant(to_double_elements ? kDoubleSize : kPointerSize)); 2182 var_to_offset.Bind(to_offset); 2183 } 2184 2185 Label next_iter(this), store_double_hole(this); 2186 Label* if_hole; 2187 if (doubles_to_objects_conversion) { 2188 // The target elements array is already preinitialized with holes, so we 2189 // can just proceed with the next iteration. 2190 if_hole = &next_iter; 2191 } else if (IsFastDoubleElementsKind(to_kind)) { 2192 if_hole = &store_double_hole; 2193 } else { 2194 // In all the other cases don't check for holes and copy the data as is. 2195 if_hole = nullptr; 2196 } 2197 2198 Node* value = LoadElementAndPrepareForStore( 2199 from_array, var_from_offset.value(), from_kind, to_kind, if_hole); 2200 2201 if (needs_write_barrier) { 2202 Store(to_array, to_offset, value); 2203 } else if (to_double_elements) { 2204 StoreNoWriteBarrier(MachineRepresentation::kFloat64, to_array, to_offset, 2205 value); 2206 } else { 2207 StoreNoWriteBarrier(MachineRepresentation::kTagged, to_array, to_offset, 2208 value); 2209 } 2210 Goto(&next_iter); 2211 2212 if (if_hole == &store_double_hole) { 2213 Bind(&store_double_hole); 2214 // Don't use doubles to store the hole double, since manipulating the 2215 // signaling NaN used for the hole in C++, e.g. with bit_cast, will 2216 // change its value on ia32 (the x87 stack is used to return values 2217 // and stores to the stack silently clear the signalling bit). 2218 // 2219 // TODO(danno): When we have a Float32/Float64 wrapper class that 2220 // preserves double bits during manipulation, remove this code/change 2221 // this to an indexed Float64 store. 2222 if (Is64()) { 2223 StoreNoWriteBarrier(MachineRepresentation::kWord64, to_array, to_offset, 2224 double_hole); 2225 } else { 2226 StoreNoWriteBarrier(MachineRepresentation::kWord32, to_array, to_offset, 2227 double_hole); 2228 StoreNoWriteBarrier(MachineRepresentation::kWord32, to_array, 2229 IntPtrAdd(to_offset, IntPtrConstant(kPointerSize)), 2230 double_hole); 2231 } 2232 Goto(&next_iter); 2233 } 2234 2235 Bind(&next_iter); 2236 Node* compare = WordNotEqual(from_offset, limit_offset); 2237 Branch(compare, &decrement, &done); 2238 } 2239 2240 Bind(&done); 2241 IncrementCounter(isolate()->counters()->inlined_copied_elements(), 1); 2242 Comment("] CopyFixedArrayElements"); 2243 } 2244 2245 void CodeStubAssembler::CopyStringCharacters(Node* from_string, Node* to_string, 2246 Node* from_index, Node* to_index, 2247 Node* character_count, 2248 String::Encoding from_encoding, 2249 String::Encoding to_encoding, 2250 ParameterMode mode) { 2251 bool from_one_byte = from_encoding == String::ONE_BYTE_ENCODING; 2252 bool to_one_byte = to_encoding == String::ONE_BYTE_ENCODING; 2253 DCHECK_IMPLIES(to_one_byte, from_one_byte); 2254 Comment("CopyStringCharacters %s -> %s", 2255 from_one_byte ? "ONE_BYTE_ENCODING" : "TWO_BYTE_ENCODING", 2256 to_one_byte ? "ONE_BYTE_ENCODING" : "TWO_BYTE_ENCODING"); 2257 2258 ElementsKind from_kind = from_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS; 2259 ElementsKind to_kind = to_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS; 2260 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize); 2261 int header_size = SeqOneByteString::kHeaderSize - kHeapObjectTag; 2262 Node* from_offset = 2263 ElementOffsetFromIndex(from_index, from_kind, mode, header_size); 2264 Node* to_offset = 2265 ElementOffsetFromIndex(to_index, to_kind, mode, header_size); 2266 Node* byte_count = ElementOffsetFromIndex(character_count, from_kind, mode); 2267 Node* limit_offset = IntPtrAdd(from_offset, byte_count); 2268 2269 // Prepare the fast loop 2270 MachineType type = 2271 from_one_byte ? MachineType::Uint8() : MachineType::Uint16(); 2272 MachineRepresentation rep = to_one_byte ? MachineRepresentation::kWord8 2273 : MachineRepresentation::kWord16; 2274 int from_increment = 1 << ElementsKindToShiftSize(from_kind); 2275 int to_increment = 1 << ElementsKindToShiftSize(to_kind); 2276 2277 Variable current_to_offset(this, MachineType::PointerRepresentation(), 2278 to_offset); 2279 VariableList vars({¤t_to_offset}, zone()); 2280 int to_index_constant = 0, from_index_constant = 0; 2281 Smi* to_index_smi = nullptr; 2282 Smi* from_index_smi = nullptr; 2283 bool index_same = (from_encoding == to_encoding) && 2284 (from_index == to_index || 2285 (ToInt32Constant(from_index, from_index_constant) && 2286 ToInt32Constant(to_index, to_index_constant) && 2287 from_index_constant == to_index_constant) || 2288 (ToSmiConstant(from_index, from_index_smi) && 2289 ToSmiConstant(to_index, to_index_smi) && 2290 to_index_smi == from_index_smi)); 2291 BuildFastLoop(vars, from_offset, limit_offset, 2292 [this, from_string, to_string, ¤t_to_offset, to_increment, 2293 type, rep, index_same](Node* offset) { 2294 Node* value = Load(type, from_string, offset); 2295 StoreNoWriteBarrier( 2296 rep, to_string, 2297 index_same ? offset : current_to_offset.value(), value); 2298 if (!index_same) { 2299 Increment(current_to_offset, to_increment); 2300 } 2301 }, 2302 from_increment, INTPTR_PARAMETERS, IndexAdvanceMode::kPost); 2303 } 2304 2305 Node* CodeStubAssembler::LoadElementAndPrepareForStore(Node* array, 2306 Node* offset, 2307 ElementsKind from_kind, 2308 ElementsKind to_kind, 2309 Label* if_hole) { 2310 if (IsFastDoubleElementsKind(from_kind)) { 2311 Node* value = 2312 LoadDoubleWithHoleCheck(array, offset, if_hole, MachineType::Float64()); 2313 if (!IsFastDoubleElementsKind(to_kind)) { 2314 value = AllocateHeapNumberWithValue(value); 2315 } 2316 return value; 2317 2318 } else { 2319 Node* value = Load(MachineType::AnyTagged(), array, offset); 2320 if (if_hole) { 2321 GotoIf(WordEqual(value, TheHoleConstant()), if_hole); 2322 } 2323 if (IsFastDoubleElementsKind(to_kind)) { 2324 if (IsFastSmiElementsKind(from_kind)) { 2325 value = SmiToFloat64(value); 2326 } else { 2327 value = LoadHeapNumberValue(value); 2328 } 2329 } 2330 return value; 2331 } 2332 } 2333 2334 Node* CodeStubAssembler::CalculateNewElementsCapacity(Node* old_capacity, 2335 ParameterMode mode) { 2336 Node* half_old_capacity = WordOrSmiShr(old_capacity, 1, mode); 2337 Node* new_capacity = IntPtrOrSmiAdd(half_old_capacity, old_capacity, mode); 2338 Node* padding = IntPtrOrSmiConstant(16, mode); 2339 return IntPtrOrSmiAdd(new_capacity, padding, mode); 2340 } 2341 2342 Node* CodeStubAssembler::TryGrowElementsCapacity(Node* object, Node* elements, 2343 ElementsKind kind, Node* key, 2344 Label* bailout) { 2345 Node* capacity = LoadFixedArrayBaseLength(elements); 2346 2347 ParameterMode mode = OptimalParameterMode(); 2348 capacity = TaggedToParameter(capacity, mode); 2349 key = TaggedToParameter(key, mode); 2350 2351 return TryGrowElementsCapacity(object, elements, kind, key, capacity, mode, 2352 bailout); 2353 } 2354 2355 Node* CodeStubAssembler::TryGrowElementsCapacity(Node* object, Node* elements, 2356 ElementsKind kind, Node* key, 2357 Node* capacity, 2358 ParameterMode mode, 2359 Label* bailout) { 2360 Comment("TryGrowElementsCapacity"); 2361 2362 // If the gap growth is too big, fall back to the runtime. 2363 Node* max_gap = IntPtrOrSmiConstant(JSObject::kMaxGap, mode); 2364 Node* max_capacity = IntPtrOrSmiAdd(capacity, max_gap, mode); 2365 GotoIf(UintPtrOrSmiGreaterThanOrEqual(key, max_capacity, mode), bailout); 2366 2367 // Calculate the capacity of the new backing store. 2368 Node* new_capacity = CalculateNewElementsCapacity( 2369 IntPtrOrSmiAdd(key, IntPtrOrSmiConstant(1, mode), mode), mode); 2370 return GrowElementsCapacity(object, elements, kind, kind, capacity, 2371 new_capacity, mode, bailout); 2372 } 2373 2374 Node* CodeStubAssembler::GrowElementsCapacity( 2375 Node* object, Node* elements, ElementsKind from_kind, ElementsKind to_kind, 2376 Node* capacity, Node* new_capacity, ParameterMode mode, Label* bailout) { 2377 Comment("[ GrowElementsCapacity"); 2378 // If size of the allocation for the new capacity doesn't fit in a page 2379 // that we can bump-pointer allocate from, fall back to the runtime. 2380 int max_size = FixedArrayBase::GetMaxLengthForNewSpaceAllocation(to_kind); 2381 GotoIf(UintPtrOrSmiGreaterThanOrEqual( 2382 new_capacity, IntPtrOrSmiConstant(max_size, mode), mode), 2383 bailout); 2384 2385 // Allocate the new backing store. 2386 Node* new_elements = AllocateFixedArray(to_kind, new_capacity, mode); 2387 2388 // Copy the elements from the old elements store to the new. 2389 // The size-check above guarantees that the |new_elements| is allocated 2390 // in new space so we can skip the write barrier. 2391 CopyFixedArrayElements(from_kind, elements, to_kind, new_elements, capacity, 2392 new_capacity, SKIP_WRITE_BARRIER, mode); 2393 2394 StoreObjectField(object, JSObject::kElementsOffset, new_elements); 2395 Comment("] GrowElementsCapacity"); 2396 return new_elements; 2397 } 2398 2399 void CodeStubAssembler::InitializeAllocationMemento(Node* base_allocation, 2400 int base_allocation_size, 2401 Node* allocation_site) { 2402 StoreObjectFieldNoWriteBarrier( 2403 base_allocation, AllocationMemento::kMapOffset + base_allocation_size, 2404 HeapConstant(Handle<Map>(isolate()->heap()->allocation_memento_map()))); 2405 StoreObjectFieldNoWriteBarrier( 2406 base_allocation, 2407 AllocationMemento::kAllocationSiteOffset + base_allocation_size, 2408 allocation_site); 2409 if (FLAG_allocation_site_pretenuring) { 2410 Node* count = LoadObjectField(allocation_site, 2411 AllocationSite::kPretenureCreateCountOffset); 2412 Node* incremented_count = SmiAdd(count, SmiConstant(Smi::FromInt(1))); 2413 StoreObjectFieldNoWriteBarrier(allocation_site, 2414 AllocationSite::kPretenureCreateCountOffset, 2415 incremented_count); 2416 } 2417 } 2418 2419 Node* CodeStubAssembler::TryTaggedToFloat64(Node* value, 2420 Label* if_valueisnotnumber) { 2421 Label out(this); 2422 Variable var_result(this, MachineRepresentation::kFloat64); 2423 2424 // Check if the {value} is a Smi or a HeapObject. 2425 Label if_valueissmi(this), if_valueisnotsmi(this); 2426 Branch(TaggedIsSmi(value), &if_valueissmi, &if_valueisnotsmi); 2427 2428 Bind(&if_valueissmi); 2429 { 2430 // Convert the Smi {value}. 2431 var_result.Bind(SmiToFloat64(value)); 2432 Goto(&out); 2433 } 2434 2435 Bind(&if_valueisnotsmi); 2436 { 2437 // Check if {value} is a HeapNumber. 2438 Label if_valueisheapnumber(this); 2439 Branch(IsHeapNumberMap(LoadMap(value)), &if_valueisheapnumber, 2440 if_valueisnotnumber); 2441 2442 Bind(&if_valueisheapnumber); 2443 { 2444 // Load the floating point value. 2445 var_result.Bind(LoadHeapNumberValue(value)); 2446 Goto(&out); 2447 } 2448 } 2449 Bind(&out); 2450 return var_result.value(); 2451 } 2452 2453 Node* CodeStubAssembler::TruncateTaggedToFloat64(Node* context, Node* value) { 2454 // We might need to loop once due to ToNumber conversion. 2455 Variable var_value(this, MachineRepresentation::kTagged), 2456 var_result(this, MachineRepresentation::kFloat64); 2457 Label loop(this, &var_value), done_loop(this, &var_result); 2458 var_value.Bind(value); 2459 Goto(&loop); 2460 Bind(&loop); 2461 { 2462 Label if_valueisnotnumber(this, Label::kDeferred); 2463 2464 // Load the current {value}. 2465 value = var_value.value(); 2466 2467 // Convert {value} to Float64 if it is a number and convert it to a number 2468 // otherwise. 2469 Node* const result = TryTaggedToFloat64(value, &if_valueisnotnumber); 2470 var_result.Bind(result); 2471 Goto(&done_loop); 2472 2473 Bind(&if_valueisnotnumber); 2474 { 2475 // Convert the {value} to a Number first. 2476 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 2477 var_value.Bind(CallStub(callable, context, value)); 2478 Goto(&loop); 2479 } 2480 } 2481 Bind(&done_loop); 2482 return var_result.value(); 2483 } 2484 2485 Node* CodeStubAssembler::TruncateTaggedToWord32(Node* context, Node* value) { 2486 // We might need to loop once due to ToNumber conversion. 2487 Variable var_value(this, MachineRepresentation::kTagged, value), 2488 var_result(this, MachineRepresentation::kWord32); 2489 Label loop(this, &var_value), done_loop(this, &var_result); 2490 Goto(&loop); 2491 Bind(&loop); 2492 { 2493 // Load the current {value}. 2494 value = var_value.value(); 2495 2496 // Check if the {value} is a Smi or a HeapObject. 2497 Label if_valueissmi(this), if_valueisnotsmi(this); 2498 Branch(TaggedIsSmi(value), &if_valueissmi, &if_valueisnotsmi); 2499 2500 Bind(&if_valueissmi); 2501 { 2502 // Convert the Smi {value}. 2503 var_result.Bind(SmiToWord32(value)); 2504 Goto(&done_loop); 2505 } 2506 2507 Bind(&if_valueisnotsmi); 2508 { 2509 // Check if {value} is a HeapNumber. 2510 Label if_valueisheapnumber(this), 2511 if_valueisnotheapnumber(this, Label::kDeferred); 2512 Branch(IsHeapNumberMap(LoadMap(value)), &if_valueisheapnumber, 2513 &if_valueisnotheapnumber); 2514 2515 Bind(&if_valueisheapnumber); 2516 { 2517 // Truncate the floating point value. 2518 var_result.Bind(TruncateHeapNumberValueToWord32(value)); 2519 Goto(&done_loop); 2520 } 2521 2522 Bind(&if_valueisnotheapnumber); 2523 { 2524 // Convert the {value} to a Number first. 2525 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 2526 var_value.Bind(CallStub(callable, context, value)); 2527 Goto(&loop); 2528 } 2529 } 2530 } 2531 Bind(&done_loop); 2532 return var_result.value(); 2533 } 2534 2535 Node* CodeStubAssembler::TruncateHeapNumberValueToWord32(Node* object) { 2536 Node* value = LoadHeapNumberValue(object); 2537 return TruncateFloat64ToWord32(value); 2538 } 2539 2540 Node* CodeStubAssembler::ChangeFloat64ToTagged(Node* value) { 2541 Node* value32 = RoundFloat64ToInt32(value); 2542 Node* value64 = ChangeInt32ToFloat64(value32); 2543 2544 Label if_valueisint32(this), if_valueisheapnumber(this), if_join(this); 2545 2546 Label if_valueisequal(this), if_valueisnotequal(this); 2547 Branch(Float64Equal(value, value64), &if_valueisequal, &if_valueisnotequal); 2548 Bind(&if_valueisequal); 2549 { 2550 GotoIfNot(Word32Equal(value32, Int32Constant(0)), &if_valueisint32); 2551 Branch(Int32LessThan(Float64ExtractHighWord32(value), Int32Constant(0)), 2552 &if_valueisheapnumber, &if_valueisint32); 2553 } 2554 Bind(&if_valueisnotequal); 2555 Goto(&if_valueisheapnumber); 2556 2557 Variable var_result(this, MachineRepresentation::kTagged); 2558 Bind(&if_valueisint32); 2559 { 2560 if (Is64()) { 2561 Node* result = SmiTag(ChangeInt32ToInt64(value32)); 2562 var_result.Bind(result); 2563 Goto(&if_join); 2564 } else { 2565 Node* pair = Int32AddWithOverflow(value32, value32); 2566 Node* overflow = Projection(1, pair); 2567 Label if_overflow(this, Label::kDeferred), if_notoverflow(this); 2568 Branch(overflow, &if_overflow, &if_notoverflow); 2569 Bind(&if_overflow); 2570 Goto(&if_valueisheapnumber); 2571 Bind(&if_notoverflow); 2572 { 2573 Node* result = BitcastWordToTaggedSigned(Projection(0, pair)); 2574 var_result.Bind(result); 2575 Goto(&if_join); 2576 } 2577 } 2578 } 2579 Bind(&if_valueisheapnumber); 2580 { 2581 Node* result = AllocateHeapNumberWithValue(value); 2582 var_result.Bind(result); 2583 Goto(&if_join); 2584 } 2585 Bind(&if_join); 2586 return var_result.value(); 2587 } 2588 2589 Node* CodeStubAssembler::ChangeInt32ToTagged(Node* value) { 2590 if (Is64()) { 2591 return SmiTag(ChangeInt32ToInt64(value)); 2592 } 2593 Variable var_result(this, MachineRepresentation::kTagged); 2594 Node* pair = Int32AddWithOverflow(value, value); 2595 Node* overflow = Projection(1, pair); 2596 Label if_overflow(this, Label::kDeferred), if_notoverflow(this), 2597 if_join(this); 2598 Branch(overflow, &if_overflow, &if_notoverflow); 2599 Bind(&if_overflow); 2600 { 2601 Node* value64 = ChangeInt32ToFloat64(value); 2602 Node* result = AllocateHeapNumberWithValue(value64); 2603 var_result.Bind(result); 2604 } 2605 Goto(&if_join); 2606 Bind(&if_notoverflow); 2607 { 2608 Node* result = BitcastWordToTaggedSigned(Projection(0, pair)); 2609 var_result.Bind(result); 2610 } 2611 Goto(&if_join); 2612 Bind(&if_join); 2613 return var_result.value(); 2614 } 2615 2616 Node* CodeStubAssembler::ChangeUint32ToTagged(Node* value) { 2617 Label if_overflow(this, Label::kDeferred), if_not_overflow(this), 2618 if_join(this); 2619 Variable var_result(this, MachineRepresentation::kTagged); 2620 // If {value} > 2^31 - 1, we need to store it in a HeapNumber. 2621 Branch(Uint32LessThan(Int32Constant(Smi::kMaxValue), value), &if_overflow, 2622 &if_not_overflow); 2623 2624 Bind(&if_not_overflow); 2625 { 2626 if (Is64()) { 2627 var_result.Bind(SmiTag(ChangeUint32ToUint64(value))); 2628 } else { 2629 // If tagging {value} results in an overflow, we need to use a HeapNumber 2630 // to represent it. 2631 Node* pair = Int32AddWithOverflow(value, value); 2632 Node* overflow = Projection(1, pair); 2633 GotoIf(overflow, &if_overflow); 2634 2635 Node* result = BitcastWordToTaggedSigned(Projection(0, pair)); 2636 var_result.Bind(result); 2637 } 2638 } 2639 Goto(&if_join); 2640 2641 Bind(&if_overflow); 2642 { 2643 Node* float64_value = ChangeUint32ToFloat64(value); 2644 var_result.Bind(AllocateHeapNumberWithValue(float64_value)); 2645 } 2646 Goto(&if_join); 2647 2648 Bind(&if_join); 2649 return var_result.value(); 2650 } 2651 2652 Node* CodeStubAssembler::ToThisString(Node* context, Node* value, 2653 char const* method_name) { 2654 Variable var_value(this, MachineRepresentation::kTagged, value); 2655 2656 // Check if the {value} is a Smi or a HeapObject. 2657 Label if_valueissmi(this, Label::kDeferred), if_valueisnotsmi(this), 2658 if_valueisstring(this); 2659 Branch(TaggedIsSmi(value), &if_valueissmi, &if_valueisnotsmi); 2660 Bind(&if_valueisnotsmi); 2661 { 2662 // Load the instance type of the {value}. 2663 Node* value_instance_type = LoadInstanceType(value); 2664 2665 // Check if the {value} is already String. 2666 Label if_valueisnotstring(this, Label::kDeferred); 2667 Branch(IsStringInstanceType(value_instance_type), &if_valueisstring, 2668 &if_valueisnotstring); 2669 Bind(&if_valueisnotstring); 2670 { 2671 // Check if the {value} is null. 2672 Label if_valueisnullorundefined(this, Label::kDeferred), 2673 if_valueisnotnullorundefined(this, Label::kDeferred), 2674 if_valueisnotnull(this, Label::kDeferred); 2675 Branch(WordEqual(value, NullConstant()), &if_valueisnullorundefined, 2676 &if_valueisnotnull); 2677 Bind(&if_valueisnotnull); 2678 { 2679 // Check if the {value} is undefined. 2680 Branch(WordEqual(value, UndefinedConstant()), 2681 &if_valueisnullorundefined, &if_valueisnotnullorundefined); 2682 Bind(&if_valueisnotnullorundefined); 2683 { 2684 // Convert the {value} to a String. 2685 Callable callable = CodeFactory::ToString(isolate()); 2686 var_value.Bind(CallStub(callable, context, value)); 2687 Goto(&if_valueisstring); 2688 } 2689 } 2690 2691 Bind(&if_valueisnullorundefined); 2692 { 2693 // The {value} is either null or undefined. 2694 CallRuntime(Runtime::kThrowCalledOnNullOrUndefined, context, 2695 HeapConstant(factory()->NewStringFromAsciiChecked( 2696 method_name, TENURED))); 2697 Unreachable(); 2698 } 2699 } 2700 } 2701 Bind(&if_valueissmi); 2702 { 2703 // The {value} is a Smi, convert it to a String. 2704 Callable callable = CodeFactory::NumberToString(isolate()); 2705 var_value.Bind(CallStub(callable, context, value)); 2706 Goto(&if_valueisstring); 2707 } 2708 Bind(&if_valueisstring); 2709 return var_value.value(); 2710 } 2711 2712 Node* CodeStubAssembler::ChangeNumberToFloat64(compiler::Node* value) { 2713 Variable result(this, MachineRepresentation::kFloat64); 2714 Label smi(this); 2715 Label done(this, &result); 2716 GotoIf(TaggedIsSmi(value), &smi); 2717 result.Bind( 2718 LoadObjectField(value, HeapNumber::kValueOffset, MachineType::Float64())); 2719 Goto(&done); 2720 2721 Bind(&smi); 2722 { 2723 result.Bind(SmiToFloat64(value)); 2724 Goto(&done); 2725 } 2726 2727 Bind(&done); 2728 return result.value(); 2729 } 2730 2731 Node* CodeStubAssembler::ToThisValue(Node* context, Node* value, 2732 PrimitiveType primitive_type, 2733 char const* method_name) { 2734 // We might need to loop once due to JSValue unboxing. 2735 Variable var_value(this, MachineRepresentation::kTagged, value); 2736 Label loop(this, &var_value), done_loop(this), 2737 done_throw(this, Label::kDeferred); 2738 Goto(&loop); 2739 Bind(&loop); 2740 { 2741 // Load the current {value}. 2742 value = var_value.value(); 2743 2744 // Check if the {value} is a Smi or a HeapObject. 2745 GotoIf(TaggedIsSmi(value), (primitive_type == PrimitiveType::kNumber) 2746 ? &done_loop 2747 : &done_throw); 2748 2749 // Load the mape of the {value}. 2750 Node* value_map = LoadMap(value); 2751 2752 // Load the instance type of the {value}. 2753 Node* value_instance_type = LoadMapInstanceType(value_map); 2754 2755 // Check if {value} is a JSValue. 2756 Label if_valueisvalue(this, Label::kDeferred), if_valueisnotvalue(this); 2757 Branch(Word32Equal(value_instance_type, Int32Constant(JS_VALUE_TYPE)), 2758 &if_valueisvalue, &if_valueisnotvalue); 2759 2760 Bind(&if_valueisvalue); 2761 { 2762 // Load the actual value from the {value}. 2763 var_value.Bind(LoadObjectField(value, JSValue::kValueOffset)); 2764 Goto(&loop); 2765 } 2766 2767 Bind(&if_valueisnotvalue); 2768 { 2769 switch (primitive_type) { 2770 case PrimitiveType::kBoolean: 2771 GotoIf(WordEqual(value_map, BooleanMapConstant()), &done_loop); 2772 break; 2773 case PrimitiveType::kNumber: 2774 GotoIf( 2775 Word32Equal(value_instance_type, Int32Constant(HEAP_NUMBER_TYPE)), 2776 &done_loop); 2777 break; 2778 case PrimitiveType::kString: 2779 GotoIf(IsStringInstanceType(value_instance_type), &done_loop); 2780 break; 2781 case PrimitiveType::kSymbol: 2782 GotoIf(Word32Equal(value_instance_type, Int32Constant(SYMBOL_TYPE)), 2783 &done_loop); 2784 break; 2785 } 2786 Goto(&done_throw); 2787 } 2788 } 2789 2790 Bind(&done_throw); 2791 { 2792 // The {value} is not a compatible receiver for this method. 2793 CallRuntime(Runtime::kThrowNotGeneric, context, 2794 HeapConstant(factory()->NewStringFromAsciiChecked(method_name, 2795 TENURED))); 2796 Unreachable(); 2797 } 2798 2799 Bind(&done_loop); 2800 return var_value.value(); 2801 } 2802 2803 Node* CodeStubAssembler::ThrowIfNotInstanceType(Node* context, Node* value, 2804 InstanceType instance_type, 2805 char const* method_name) { 2806 Label out(this), throw_exception(this, Label::kDeferred); 2807 Variable var_value_map(this, MachineRepresentation::kTagged); 2808 2809 GotoIf(TaggedIsSmi(value), &throw_exception); 2810 2811 // Load the instance type of the {value}. 2812 var_value_map.Bind(LoadMap(value)); 2813 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); 2814 2815 Branch(Word32Equal(value_instance_type, Int32Constant(instance_type)), &out, 2816 &throw_exception); 2817 2818 // The {value} is not a compatible receiver for this method. 2819 Bind(&throw_exception); 2820 CallRuntime( 2821 Runtime::kThrowIncompatibleMethodReceiver, context, 2822 HeapConstant(factory()->NewStringFromAsciiChecked(method_name, TENURED)), 2823 value); 2824 Unreachable(); 2825 2826 Bind(&out); 2827 return var_value_map.value(); 2828 } 2829 2830 Node* CodeStubAssembler::InstanceTypeEqual(Node* instance_type, int type) { 2831 return Word32Equal(instance_type, Int32Constant(type)); 2832 } 2833 2834 Node* CodeStubAssembler::IsSpecialReceiverMap(Node* map) { 2835 Node* is_special = IsSpecialReceiverInstanceType(LoadMapInstanceType(map)); 2836 uint32_t mask = 2837 1 << Map::kHasNamedInterceptor | 1 << Map::kIsAccessCheckNeeded; 2838 USE(mask); 2839 // Interceptors or access checks imply special receiver. 2840 CSA_ASSERT(this, 2841 SelectConstant(IsSetWord32(LoadMapBitField(map), mask), is_special, 2842 Int32Constant(1), MachineRepresentation::kWord32)); 2843 return is_special; 2844 } 2845 2846 Node* CodeStubAssembler::IsDictionaryMap(Node* map) { 2847 CSA_SLOW_ASSERT(this, IsMap(map)); 2848 Node* bit_field3 = LoadMapBitField3(map); 2849 return Word32NotEqual(IsSetWord32<Map::DictionaryMap>(bit_field3), 2850 Int32Constant(0)); 2851 } 2852 2853 Node* CodeStubAssembler::IsCallableMap(Node* map) { 2854 CSA_ASSERT(this, IsMap(map)); 2855 return Word32NotEqual( 2856 Word32And(LoadMapBitField(map), Int32Constant(1 << Map::kIsCallable)), 2857 Int32Constant(0)); 2858 } 2859 2860 Node* CodeStubAssembler::IsCallable(Node* object) { 2861 return IsCallableMap(LoadMap(object)); 2862 } 2863 2864 Node* CodeStubAssembler::IsConstructorMap(Node* map) { 2865 CSA_ASSERT(this, IsMap(map)); 2866 return Word32NotEqual( 2867 Word32And(LoadMapBitField(map), Int32Constant(1 << Map::kIsConstructor)), 2868 Int32Constant(0)); 2869 } 2870 2871 Node* CodeStubAssembler::IsSpecialReceiverInstanceType(Node* instance_type) { 2872 STATIC_ASSERT(JS_GLOBAL_OBJECT_TYPE <= LAST_SPECIAL_RECEIVER_TYPE); 2873 return Int32LessThanOrEqual(instance_type, 2874 Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)); 2875 } 2876 2877 Node* CodeStubAssembler::IsStringInstanceType(Node* instance_type) { 2878 STATIC_ASSERT(INTERNALIZED_STRING_TYPE == FIRST_TYPE); 2879 return Int32LessThan(instance_type, Int32Constant(FIRST_NONSTRING_TYPE)); 2880 } 2881 2882 Node* CodeStubAssembler::IsJSReceiverInstanceType(Node* instance_type) { 2883 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 2884 return Int32GreaterThanOrEqual(instance_type, 2885 Int32Constant(FIRST_JS_RECEIVER_TYPE)); 2886 } 2887 2888 Node* CodeStubAssembler::IsJSReceiver(Node* object) { 2889 STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); 2890 return IsJSReceiverInstanceType(LoadInstanceType(object)); 2891 } 2892 2893 Node* CodeStubAssembler::IsJSReceiverMap(Node* map) { 2894 STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); 2895 return IsJSReceiverInstanceType(LoadMapInstanceType(map)); 2896 } 2897 2898 Node* CodeStubAssembler::IsJSObject(Node* object) { 2899 STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); 2900 return Int32GreaterThanOrEqual(LoadInstanceType(object), 2901 Int32Constant(FIRST_JS_RECEIVER_TYPE)); 2902 } 2903 2904 Node* CodeStubAssembler::IsJSGlobalProxy(Node* object) { 2905 return Word32Equal(LoadInstanceType(object), 2906 Int32Constant(JS_GLOBAL_PROXY_TYPE)); 2907 } 2908 2909 Node* CodeStubAssembler::IsMap(Node* map) { 2910 return HasInstanceType(map, MAP_TYPE); 2911 } 2912 2913 Node* CodeStubAssembler::IsJSValue(Node* map) { 2914 return HasInstanceType(map, JS_VALUE_TYPE); 2915 } 2916 2917 Node* CodeStubAssembler::IsJSArray(Node* object) { 2918 return HasInstanceType(object, JS_ARRAY_TYPE); 2919 } 2920 2921 Node* CodeStubAssembler::IsWeakCell(Node* object) { 2922 return HasInstanceType(object, WEAK_CELL_TYPE); 2923 } 2924 2925 Node* CodeStubAssembler::IsBoolean(Node* object) { 2926 return IsBooleanMap(LoadMap(object)); 2927 } 2928 2929 Node* CodeStubAssembler::IsHeapNumber(Node* object) { 2930 return IsHeapNumberMap(LoadMap(object)); 2931 } 2932 2933 Node* CodeStubAssembler::IsName(Node* object) { 2934 return Int32LessThanOrEqual(LoadInstanceType(object), 2935 Int32Constant(LAST_NAME_TYPE)); 2936 } 2937 2938 Node* CodeStubAssembler::IsString(Node* object) { 2939 return Int32LessThanOrEqual(LoadInstanceType(object), 2940 Int32Constant(FIRST_NONSTRING_TYPE)); 2941 } 2942 2943 Node* CodeStubAssembler::IsSymbol(Node* object) { 2944 return IsSymbolMap(LoadMap(object)); 2945 } 2946 2947 Node* CodeStubAssembler::IsPrivateSymbol(Node* object) { 2948 return Select( 2949 IsSymbol(object), 2950 [=] { 2951 Node* const flags = 2952 SmiToWord32(LoadObjectField(object, Symbol::kFlagsOffset)); 2953 const int kPrivateMask = 1 << Symbol::kPrivateBit; 2954 return IsSetWord32(flags, kPrivateMask); 2955 }, 2956 [=] { return Int32Constant(0); }, MachineRepresentation::kWord32); 2957 } 2958 2959 Node* CodeStubAssembler::IsNativeContext(Node* object) { 2960 return WordEqual(LoadMap(object), LoadRoot(Heap::kNativeContextMapRootIndex)); 2961 } 2962 2963 Node* CodeStubAssembler::IsFixedDoubleArray(Node* object) { 2964 return WordEqual(LoadMap(object), FixedDoubleArrayMapConstant()); 2965 } 2966 2967 Node* CodeStubAssembler::IsHashTable(Node* object) { 2968 return WordEqual(LoadMap(object), LoadRoot(Heap::kHashTableMapRootIndex)); 2969 } 2970 2971 Node* CodeStubAssembler::IsDictionary(Node* object) { 2972 return Word32Or(IsHashTable(object), IsUnseededNumberDictionary(object)); 2973 } 2974 2975 Node* CodeStubAssembler::IsUnseededNumberDictionary(Node* object) { 2976 return WordEqual(LoadMap(object), 2977 LoadRoot(Heap::kUnseededNumberDictionaryMapRootIndex)); 2978 } 2979 2980 Node* CodeStubAssembler::IsJSFunction(Node* object) { 2981 return HasInstanceType(object, JS_FUNCTION_TYPE); 2982 } 2983 2984 Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index, 2985 ParameterMode parameter_mode) { 2986 CSA_ASSERT(this, IsString(string)); 2987 // Translate the {index} into a Word. 2988 index = ParameterToWord(index, parameter_mode); 2989 2990 // We may need to loop in case of cons, thin, or sliced strings. 2991 Variable var_index(this, MachineType::PointerRepresentation(), index); 2992 Variable var_string(this, MachineRepresentation::kTagged, string); 2993 Variable var_result(this, MachineRepresentation::kWord32); 2994 Variable* loop_vars[] = {&var_index, &var_string}; 2995 Label done_loop(this, &var_result), loop(this, 2, loop_vars); 2996 Goto(&loop); 2997 Bind(&loop); 2998 { 2999 // Load the current {index}. 3000 index = var_index.value(); 3001 3002 // Load the current {string}. 3003 string = var_string.value(); 3004 3005 // Load the instance type of the {string}. 3006 Node* string_instance_type = LoadInstanceType(string); 3007 3008 // Check if the {string} is a SeqString. 3009 Label if_stringissequential(this), if_stringisnotsequential(this); 3010 Branch(Word32Equal(Word32And(string_instance_type, 3011 Int32Constant(kStringRepresentationMask)), 3012 Int32Constant(kSeqStringTag)), 3013 &if_stringissequential, &if_stringisnotsequential); 3014 3015 Bind(&if_stringissequential); 3016 { 3017 // Check if the {string} is a TwoByteSeqString or a OneByteSeqString. 3018 Label if_stringistwobyte(this), if_stringisonebyte(this); 3019 Branch(Word32Equal(Word32And(string_instance_type, 3020 Int32Constant(kStringEncodingMask)), 3021 Int32Constant(kTwoByteStringTag)), 3022 &if_stringistwobyte, &if_stringisonebyte); 3023 3024 Bind(&if_stringisonebyte); 3025 { 3026 var_result.Bind( 3027 Load(MachineType::Uint8(), string, 3028 IntPtrAdd(index, IntPtrConstant(SeqOneByteString::kHeaderSize - 3029 kHeapObjectTag)))); 3030 Goto(&done_loop); 3031 } 3032 3033 Bind(&if_stringistwobyte); 3034 { 3035 var_result.Bind( 3036 Load(MachineType::Uint16(), string, 3037 IntPtrAdd(WordShl(index, IntPtrConstant(1)), 3038 IntPtrConstant(SeqTwoByteString::kHeaderSize - 3039 kHeapObjectTag)))); 3040 Goto(&done_loop); 3041 } 3042 } 3043 3044 Bind(&if_stringisnotsequential); 3045 { 3046 // Check if the {string} is a ConsString. 3047 Label if_stringiscons(this), if_stringisnotcons(this); 3048 Branch(Word32Equal(Word32And(string_instance_type, 3049 Int32Constant(kStringRepresentationMask)), 3050 Int32Constant(kConsStringTag)), 3051 &if_stringiscons, &if_stringisnotcons); 3052 3053 Bind(&if_stringiscons); 3054 { 3055 // Check whether the right hand side is the empty string (i.e. if 3056 // this is really a flat string in a cons string). If that is not 3057 // the case we flatten the string first. 3058 Label if_rhsisempty(this), if_rhsisnotempty(this, Label::kDeferred); 3059 Node* rhs = LoadObjectField(string, ConsString::kSecondOffset); 3060 Branch(WordEqual(rhs, EmptyStringConstant()), &if_rhsisempty, 3061 &if_rhsisnotempty); 3062 3063 Bind(&if_rhsisempty); 3064 { 3065 // Just operate on the left hand side of the {string}. 3066 var_string.Bind(LoadObjectField(string, ConsString::kFirstOffset)); 3067 Goto(&loop); 3068 } 3069 3070 Bind(&if_rhsisnotempty); 3071 { 3072 // Flatten the {string} and lookup in the resulting string. 3073 var_string.Bind(CallRuntime(Runtime::kFlattenString, 3074 NoContextConstant(), string)); 3075 Goto(&loop); 3076 } 3077 } 3078 3079 Bind(&if_stringisnotcons); 3080 { 3081 // Check if the {string} is an ExternalString. 3082 Label if_stringisexternal(this), if_stringisnotexternal(this); 3083 Branch(Word32Equal(Word32And(string_instance_type, 3084 Int32Constant(kStringRepresentationMask)), 3085 Int32Constant(kExternalStringTag)), 3086 &if_stringisexternal, &if_stringisnotexternal); 3087 3088 Bind(&if_stringisexternal); 3089 { 3090 // Check if the {string} is a short external string. 3091 Label if_stringisnotshort(this), 3092 if_stringisshort(this, Label::kDeferred); 3093 Branch(Word32Equal(Word32And(string_instance_type, 3094 Int32Constant(kShortExternalStringMask)), 3095 Int32Constant(0)), 3096 &if_stringisnotshort, &if_stringisshort); 3097 3098 Bind(&if_stringisnotshort); 3099 { 3100 // Load the actual resource data from the {string}. 3101 Node* string_resource_data = 3102 LoadObjectField(string, ExternalString::kResourceDataOffset, 3103 MachineType::Pointer()); 3104 3105 // Check if the {string} is a TwoByteExternalString or a 3106 // OneByteExternalString. 3107 Label if_stringistwobyte(this), if_stringisonebyte(this); 3108 Branch(Word32Equal(Word32And(string_instance_type, 3109 Int32Constant(kStringEncodingMask)), 3110 Int32Constant(kTwoByteStringTag)), 3111 &if_stringistwobyte, &if_stringisonebyte); 3112 3113 Bind(&if_stringisonebyte); 3114 { 3115 var_result.Bind( 3116 Load(MachineType::Uint8(), string_resource_data, index)); 3117 Goto(&done_loop); 3118 } 3119 3120 Bind(&if_stringistwobyte); 3121 { 3122 var_result.Bind(Load(MachineType::Uint16(), string_resource_data, 3123 WordShl(index, IntPtrConstant(1)))); 3124 Goto(&done_loop); 3125 } 3126 } 3127 3128 Bind(&if_stringisshort); 3129 { 3130 // The {string} might be compressed, call the runtime. 3131 var_result.Bind(SmiToWord32( 3132 CallRuntime(Runtime::kExternalStringGetChar, 3133 NoContextConstant(), string, SmiTag(index)))); 3134 Goto(&done_loop); 3135 } 3136 } 3137 3138 Bind(&if_stringisnotexternal); 3139 { 3140 Label if_stringissliced(this), if_stringisthin(this); 3141 Branch( 3142 Word32Equal(Word32And(string_instance_type, 3143 Int32Constant(kStringRepresentationMask)), 3144 Int32Constant(kSlicedStringTag)), 3145 &if_stringissliced, &if_stringisthin); 3146 Bind(&if_stringissliced); 3147 { 3148 // The {string} is a SlicedString, continue with its parent. 3149 Node* string_offset = 3150 LoadAndUntagObjectField(string, SlicedString::kOffsetOffset); 3151 Node* string_parent = 3152 LoadObjectField(string, SlicedString::kParentOffset); 3153 var_index.Bind(IntPtrAdd(index, string_offset)); 3154 var_string.Bind(string_parent); 3155 Goto(&loop); 3156 } 3157 Bind(&if_stringisthin); 3158 { 3159 // The {string} is a ThinString, continue with its actual value. 3160 var_string.Bind(LoadObjectField(string, ThinString::kActualOffset)); 3161 Goto(&loop); 3162 } 3163 } 3164 } 3165 } 3166 } 3167 3168 Bind(&done_loop); 3169 return var_result.value(); 3170 } 3171 3172 Node* CodeStubAssembler::StringFromCharCode(Node* code) { 3173 Variable var_result(this, MachineRepresentation::kTagged); 3174 3175 // Check if the {code} is a one-byte char code. 3176 Label if_codeisonebyte(this), if_codeistwobyte(this, Label::kDeferred), 3177 if_done(this); 3178 Branch(Int32LessThanOrEqual(code, Int32Constant(String::kMaxOneByteCharCode)), 3179 &if_codeisonebyte, &if_codeistwobyte); 3180 Bind(&if_codeisonebyte); 3181 { 3182 // Load the isolate wide single character string cache. 3183 Node* cache = LoadRoot(Heap::kSingleCharacterStringCacheRootIndex); 3184 Node* code_index = ChangeUint32ToWord(code); 3185 3186 // Check if we have an entry for the {code} in the single character string 3187 // cache already. 3188 Label if_entryisundefined(this, Label::kDeferred), 3189 if_entryisnotundefined(this); 3190 Node* entry = LoadFixedArrayElement(cache, code_index); 3191 Branch(WordEqual(entry, UndefinedConstant()), &if_entryisundefined, 3192 &if_entryisnotundefined); 3193 3194 Bind(&if_entryisundefined); 3195 { 3196 // Allocate a new SeqOneByteString for {code} and store it in the {cache}. 3197 Node* result = AllocateSeqOneByteString(1); 3198 StoreNoWriteBarrier( 3199 MachineRepresentation::kWord8, result, 3200 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag), code); 3201 StoreFixedArrayElement(cache, code_index, result); 3202 var_result.Bind(result); 3203 Goto(&if_done); 3204 } 3205 3206 Bind(&if_entryisnotundefined); 3207 { 3208 // Return the entry from the {cache}. 3209 var_result.Bind(entry); 3210 Goto(&if_done); 3211 } 3212 } 3213 3214 Bind(&if_codeistwobyte); 3215 { 3216 // Allocate a new SeqTwoByteString for {code}. 3217 Node* result = AllocateSeqTwoByteString(1); 3218 StoreNoWriteBarrier( 3219 MachineRepresentation::kWord16, result, 3220 IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag), code); 3221 var_result.Bind(result); 3222 Goto(&if_done); 3223 } 3224 3225 Bind(&if_done); 3226 return var_result.value(); 3227 } 3228 3229 namespace { 3230 3231 // A wrapper around CopyStringCharacters which determines the correct string 3232 // encoding, allocates a corresponding sequential string, and then copies the 3233 // given character range using CopyStringCharacters. 3234 // |from_string| must be a sequential string. |from_index| and 3235 // |character_count| must be Smis s.t. 3236 // 0 <= |from_index| <= |from_index| + |character_count| < from_string.length. 3237 Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context, 3238 Node* from, Node* from_instance_type, 3239 Node* from_index, Node* character_count) { 3240 typedef CodeStubAssembler::Label Label; 3241 typedef CodeStubAssembler::Variable Variable; 3242 3243 Label end(a), two_byte_sequential(a); 3244 Variable var_result(a, MachineRepresentation::kTagged); 3245 3246 Node* const smi_zero = a->SmiConstant(Smi::kZero); 3247 3248 STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0); 3249 a->GotoIf(a->Word32Equal(a->Word32And(from_instance_type, 3250 a->Int32Constant(kStringEncodingMask)), 3251 a->Int32Constant(0)), 3252 &two_byte_sequential); 3253 3254 // The subject string is a sequential one-byte string. 3255 { 3256 Node* result = 3257 a->AllocateSeqOneByteString(context, a->SmiToWord(character_count)); 3258 a->CopyStringCharacters(from, result, from_index, smi_zero, character_count, 3259 String::ONE_BYTE_ENCODING, 3260 String::ONE_BYTE_ENCODING, 3261 CodeStubAssembler::SMI_PARAMETERS); 3262 var_result.Bind(result); 3263 3264 a->Goto(&end); 3265 } 3266 3267 // The subject string is a sequential two-byte string. 3268 a->Bind(&two_byte_sequential); 3269 { 3270 Node* result = 3271 a->AllocateSeqTwoByteString(context, a->SmiToWord(character_count)); 3272 a->CopyStringCharacters(from, result, from_index, smi_zero, character_count, 3273 String::TWO_BYTE_ENCODING, 3274 String::TWO_BYTE_ENCODING, 3275 CodeStubAssembler::SMI_PARAMETERS); 3276 var_result.Bind(result); 3277 3278 a->Goto(&end); 3279 } 3280 3281 a->Bind(&end); 3282 return var_result.value(); 3283 } 3284 3285 } // namespace 3286 3287 Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, 3288 Node* to) { 3289 Label end(this); 3290 Label runtime(this); 3291 3292 Node* const int_zero = Int32Constant(0); 3293 3294 // Int32 variables. 3295 Variable var_instance_type(this, MachineRepresentation::kWord32, int_zero); 3296 Variable var_representation(this, MachineRepresentation::kWord32, int_zero); 3297 3298 Variable var_from(this, MachineRepresentation::kTagged, from); // Smi. 3299 Variable var_string(this, MachineRepresentation::kTagged, string); // String. 3300 Variable var_result(this, MachineRepresentation::kTagged); // String. 3301 3302 // Make sure first argument is a string. 3303 CSA_ASSERT(this, TaggedIsNotSmi(string)); 3304 CSA_ASSERT(this, IsString(string)); 3305 3306 // Load the instance type of the {string}. 3307 Node* const instance_type = LoadInstanceType(string); 3308 var_instance_type.Bind(instance_type); 3309 3310 // Make sure that both from and to are non-negative smis. 3311 3312 GotoIfNot(TaggedIsPositiveSmi(from), &runtime); 3313 GotoIfNot(TaggedIsPositiveSmi(to), &runtime); 3314 3315 Node* const substr_length = SmiSub(to, from); 3316 Node* const string_length = LoadStringLength(string); 3317 3318 // Begin dispatching based on substring length. 3319 3320 Label original_string_or_invalid_length(this); 3321 GotoIf(SmiAboveOrEqual(substr_length, string_length), 3322 &original_string_or_invalid_length); 3323 3324 // A real substring (substr_length < string_length). 3325 3326 Label single_char(this); 3327 GotoIf(SmiEqual(substr_length, SmiConstant(Smi::FromInt(1))), &single_char); 3328 3329 // TODO(jgruber): Add an additional case for substring of length == 0? 3330 3331 // Deal with different string types: update the index if necessary 3332 // and put the underlying string into var_string. 3333 3334 // If the string is not indirect, it can only be sequential or external. 3335 STATIC_ASSERT(kIsIndirectStringMask == 3336 (kSlicedStringTag & kConsStringTag & kThinStringTag)); 3337 STATIC_ASSERT(kIsIndirectStringMask != 0); 3338 Label underlying_unpacked(this); 3339 GotoIf(Word32Equal( 3340 Word32And(instance_type, Int32Constant(kIsIndirectStringMask)), 3341 Int32Constant(0)), 3342 &underlying_unpacked); 3343 3344 // The subject string is a sliced, cons, or thin string. 3345 3346 Label thin_string(this), thin_or_sliced(this); 3347 var_representation.Bind( 3348 Word32And(instance_type, Int32Constant(kStringRepresentationMask))); 3349 GotoIf( 3350 Word32NotEqual(var_representation.value(), Int32Constant(kConsStringTag)), 3351 &thin_or_sliced); 3352 3353 // Cons string. Check whether it is flat, then fetch first part. 3354 // Flat cons strings have an empty second part. 3355 { 3356 GotoIf(WordNotEqual(LoadObjectField(string, ConsString::kSecondOffset), 3357 EmptyStringConstant()), 3358 &runtime); 3359 3360 Node* first_string_part = LoadObjectField(string, ConsString::kFirstOffset); 3361 var_string.Bind(first_string_part); 3362 var_instance_type.Bind(LoadInstanceType(first_string_part)); 3363 var_representation.Bind(Word32And( 3364 var_instance_type.value(), Int32Constant(kStringRepresentationMask))); 3365 3366 // The loaded first part might be a thin string. 3367 Branch(Word32Equal(Word32And(var_instance_type.value(), 3368 Int32Constant(kIsIndirectStringMask)), 3369 Int32Constant(0)), 3370 &underlying_unpacked, &thin_string); 3371 } 3372 3373 Bind(&thin_or_sliced); 3374 { 3375 GotoIf( 3376 Word32Equal(var_representation.value(), Int32Constant(kThinStringTag)), 3377 &thin_string); 3378 // Otherwise it's a sliced string. 3379 // Fetch parent and correct start index by offset. 3380 Node* sliced_offset = 3381 LoadObjectField(var_string.value(), SlicedString::kOffsetOffset); 3382 var_from.Bind(SmiAdd(from, sliced_offset)); 3383 3384 Node* slice_parent = LoadObjectField(string, SlicedString::kParentOffset); 3385 var_string.Bind(slice_parent); 3386 3387 Node* slice_parent_instance_type = LoadInstanceType(slice_parent); 3388 var_instance_type.Bind(slice_parent_instance_type); 3389 3390 // The loaded parent might be a thin string. 3391 Branch(Word32Equal(Word32And(var_instance_type.value(), 3392 Int32Constant(kIsIndirectStringMask)), 3393 Int32Constant(0)), 3394 &underlying_unpacked, &thin_string); 3395 } 3396 3397 Bind(&thin_string); 3398 { 3399 Node* actual_string = 3400 LoadObjectField(var_string.value(), ThinString::kActualOffset); 3401 var_string.Bind(actual_string); 3402 var_instance_type.Bind(LoadInstanceType(actual_string)); 3403 Goto(&underlying_unpacked); 3404 } 3405 3406 // The subject string can only be external or sequential string of either 3407 // encoding at this point. 3408 Label external_string(this); 3409 Bind(&underlying_unpacked); 3410 { 3411 if (FLAG_string_slices) { 3412 Label copy_routine(this); 3413 3414 // Short slice. Copy instead of slicing. 3415 GotoIf(SmiLessThan(substr_length, 3416 SmiConstant(Smi::FromInt(SlicedString::kMinLength))), 3417 ©_routine); 3418 3419 // Allocate new sliced string. 3420 3421 Label two_byte_slice(this); 3422 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); 3423 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); 3424 3425 Counters* counters = isolate()->counters(); 3426 IncrementCounter(counters->sub_string_native(), 1); 3427 3428 GotoIf(Word32Equal(Word32And(var_instance_type.value(), 3429 Int32Constant(kStringEncodingMask)), 3430 Int32Constant(0)), 3431 &two_byte_slice); 3432 3433 var_result.Bind(AllocateSlicedOneByteString( 3434 substr_length, var_string.value(), var_from.value())); 3435 Goto(&end); 3436 3437 Bind(&two_byte_slice); 3438 3439 var_result.Bind(AllocateSlicedTwoByteString( 3440 substr_length, var_string.value(), var_from.value())); 3441 Goto(&end); 3442 3443 Bind(©_routine); 3444 } 3445 3446 // The subject string can only be external or sequential string of either 3447 // encoding at this point. 3448 STATIC_ASSERT(kExternalStringTag != 0); 3449 STATIC_ASSERT(kSeqStringTag == 0); 3450 GotoIfNot(Word32Equal(Word32And(var_instance_type.value(), 3451 Int32Constant(kExternalStringTag)), 3452 Int32Constant(0)), 3453 &external_string); 3454 3455 var_result.Bind(AllocAndCopyStringCharacters( 3456 this, context, var_string.value(), var_instance_type.value(), 3457 var_from.value(), substr_length)); 3458 3459 Counters* counters = isolate()->counters(); 3460 IncrementCounter(counters->sub_string_native(), 1); 3461 3462 Goto(&end); 3463 } 3464 3465 // Handle external string. 3466 Bind(&external_string); 3467 { 3468 Node* const fake_sequential_string = TryDerefExternalString( 3469 var_string.value(), var_instance_type.value(), &runtime); 3470 3471 var_result.Bind(AllocAndCopyStringCharacters( 3472 this, context, fake_sequential_string, var_instance_type.value(), 3473 var_from.value(), substr_length)); 3474 3475 Counters* counters = isolate()->counters(); 3476 IncrementCounter(counters->sub_string_native(), 1); 3477 3478 Goto(&end); 3479 } 3480 3481 // Substrings of length 1 are generated through CharCodeAt and FromCharCode. 3482 Bind(&single_char); 3483 { 3484 Node* char_code = StringCharCodeAt(var_string.value(), var_from.value()); 3485 var_result.Bind(StringFromCharCode(char_code)); 3486 Goto(&end); 3487 } 3488 3489 Bind(&original_string_or_invalid_length); 3490 { 3491 // Longer than original string's length or negative: unsafe arguments. 3492 GotoIf(SmiAbove(substr_length, string_length), &runtime); 3493 3494 // Equal length - check if {from, to} == {0, str.length}. 3495 GotoIf(SmiAbove(from, SmiConstant(Smi::kZero)), &runtime); 3496 3497 // Return the original string (substr_length == string_length). 3498 3499 Counters* counters = isolate()->counters(); 3500 IncrementCounter(counters->sub_string_native(), 1); 3501 3502 var_result.Bind(string); 3503 Goto(&end); 3504 } 3505 3506 // Fall back to a runtime call. 3507 Bind(&runtime); 3508 { 3509 var_result.Bind( 3510 CallRuntime(Runtime::kSubString, context, string, from, to)); 3511 Goto(&end); 3512 } 3513 3514 Bind(&end); 3515 return var_result.value(); 3516 } 3517 3518 namespace { 3519 3520 Node* IsExternalStringInstanceType(CodeStubAssembler* a, 3521 Node* const instance_type) { 3522 CSA_ASSERT(a, a->IsStringInstanceType(instance_type)); 3523 return a->Word32Equal( 3524 a->Word32And(instance_type, a->Int32Constant(kStringRepresentationMask)), 3525 a->Int32Constant(kExternalStringTag)); 3526 } 3527 3528 Node* IsShortExternalStringInstanceType(CodeStubAssembler* a, 3529 Node* const instance_type) { 3530 CSA_ASSERT(a, a->IsStringInstanceType(instance_type)); 3531 STATIC_ASSERT(kShortExternalStringTag != 0); 3532 return a->Word32NotEqual( 3533 a->Word32And(instance_type, a->Int32Constant(kShortExternalStringMask)), 3534 a->Int32Constant(0)); 3535 } 3536 3537 } // namespace 3538 3539 Node* CodeStubAssembler::TryDerefExternalString(Node* const string, 3540 Node* const instance_type, 3541 Label* if_bailout) { 3542 Label out(this); 3543 3544 USE(IsExternalStringInstanceType); 3545 CSA_ASSERT(this, IsExternalStringInstanceType(this, instance_type)); 3546 GotoIf(IsShortExternalStringInstanceType(this, instance_type), if_bailout); 3547 3548 // Move the pointer so that offset-wise, it looks like a sequential string. 3549 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); 3550 3551 Node* resource_data = LoadObjectField( 3552 string, ExternalString::kResourceDataOffset, MachineType::Pointer()); 3553 Node* const fake_sequential_string = 3554 IntPtrSub(resource_data, 3555 IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); 3556 3557 return fake_sequential_string; 3558 } 3559 3560 void CodeStubAssembler::MaybeDerefIndirectString(Variable* var_string, 3561 Node* instance_type, 3562 Variable* var_did_something) { 3563 Label deref(this), done(this, var_did_something); 3564 Node* representation = 3565 Word32And(instance_type, Int32Constant(kStringRepresentationMask)); 3566 GotoIf(Word32Equal(representation, Int32Constant(kThinStringTag)), &deref); 3567 GotoIf(Word32NotEqual(representation, Int32Constant(kConsStringTag)), &done); 3568 // Cons string. 3569 Node* rhs = LoadObjectField(var_string->value(), ConsString::kSecondOffset); 3570 GotoIf(WordEqual(rhs, EmptyStringConstant()), &deref); 3571 Goto(&done); 3572 3573 Bind(&deref); 3574 STATIC_ASSERT(ThinString::kActualOffset == ConsString::kFirstOffset); 3575 var_string->Bind( 3576 LoadObjectField(var_string->value(), ThinString::kActualOffset)); 3577 var_did_something->Bind(IntPtrConstant(1)); 3578 Goto(&done); 3579 3580 Bind(&done); 3581 } 3582 3583 void CodeStubAssembler::MaybeDerefIndirectStrings(Variable* var_left, 3584 Node* left_instance_type, 3585 Variable* var_right, 3586 Node* right_instance_type, 3587 Label* did_something) { 3588 Variable var_did_something(this, MachineType::PointerRepresentation(), 3589 IntPtrConstant(0)); 3590 MaybeDerefIndirectString(var_left, left_instance_type, &var_did_something); 3591 MaybeDerefIndirectString(var_right, right_instance_type, &var_did_something); 3592 3593 GotoIf(WordNotEqual(var_did_something.value(), IntPtrConstant(0)), 3594 did_something); 3595 // Fall through if neither string was an indirect string. 3596 } 3597 3598 Node* CodeStubAssembler::StringAdd(Node* context, Node* left, Node* right, 3599 AllocationFlags flags) { 3600 Label check_right(this); 3601 Label runtime(this, Label::kDeferred); 3602 Label cons(this); 3603 Variable result(this, MachineRepresentation::kTagged); 3604 Label done(this, &result); 3605 Label done_native(this, &result); 3606 Counters* counters = isolate()->counters(); 3607 3608 Node* left_length = LoadStringLength(left); 3609 GotoIf(WordNotEqual(IntPtrConstant(0), left_length), &check_right); 3610 result.Bind(right); 3611 Goto(&done_native); 3612 3613 Bind(&check_right); 3614 Node* right_length = LoadStringLength(right); 3615 GotoIf(WordNotEqual(IntPtrConstant(0), right_length), &cons); 3616 result.Bind(left); 3617 Goto(&done_native); 3618 3619 Bind(&cons); 3620 { 3621 CSA_ASSERT(this, TaggedIsSmi(left_length)); 3622 CSA_ASSERT(this, TaggedIsSmi(right_length)); 3623 Node* new_length = SmiAdd(left_length, right_length); 3624 GotoIf(SmiAboveOrEqual(new_length, SmiConstant(String::kMaxLength)), 3625 &runtime); 3626 3627 Variable var_left(this, MachineRepresentation::kTagged, left); 3628 Variable var_right(this, MachineRepresentation::kTagged, right); 3629 Variable* input_vars[2] = {&var_left, &var_right}; 3630 Label non_cons(this, 2, input_vars); 3631 Label slow(this, Label::kDeferred); 3632 GotoIf(SmiLessThan(new_length, SmiConstant(ConsString::kMinLength)), 3633 &non_cons); 3634 3635 result.Bind(NewConsString(context, new_length, var_left.value(), 3636 var_right.value(), flags)); 3637 Goto(&done_native); 3638 3639 Bind(&non_cons); 3640 3641 Comment("Full string concatenate"); 3642 Node* left_instance_type = LoadInstanceType(var_left.value()); 3643 Node* right_instance_type = LoadInstanceType(var_right.value()); 3644 // Compute intersection and difference of instance types. 3645 3646 Node* ored_instance_types = 3647 Word32Or(left_instance_type, right_instance_type); 3648 Node* xored_instance_types = 3649 Word32Xor(left_instance_type, right_instance_type); 3650 3651 // Check if both strings have the same encoding and both are sequential. 3652 GotoIf(Word32NotEqual(Word32And(xored_instance_types, 3653 Int32Constant(kStringEncodingMask)), 3654 Int32Constant(0)), 3655 &runtime); 3656 GotoIf(Word32NotEqual(Word32And(ored_instance_types, 3657 Int32Constant(kStringRepresentationMask)), 3658 Int32Constant(0)), 3659 &slow); 3660 3661 Label two_byte(this); 3662 GotoIf(Word32Equal(Word32And(ored_instance_types, 3663 Int32Constant(kStringEncodingMask)), 3664 Int32Constant(kTwoByteStringTag)), 3665 &two_byte); 3666 // One-byte sequential string case 3667 Node* new_string = 3668 AllocateSeqOneByteString(context, new_length, SMI_PARAMETERS); 3669 CopyStringCharacters(var_left.value(), new_string, SmiConstant(Smi::kZero), 3670 SmiConstant(Smi::kZero), left_length, 3671 String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING, 3672 SMI_PARAMETERS); 3673 CopyStringCharacters(var_right.value(), new_string, SmiConstant(Smi::kZero), 3674 left_length, right_length, String::ONE_BYTE_ENCODING, 3675 String::ONE_BYTE_ENCODING, SMI_PARAMETERS); 3676 result.Bind(new_string); 3677 Goto(&done_native); 3678 3679 Bind(&two_byte); 3680 { 3681 // Two-byte sequential string case 3682 new_string = 3683 AllocateSeqTwoByteString(context, new_length, SMI_PARAMETERS); 3684 CopyStringCharacters(var_left.value(), new_string, 3685 SmiConstant(Smi::kZero), SmiConstant(Smi::kZero), 3686 left_length, String::TWO_BYTE_ENCODING, 3687 String::TWO_BYTE_ENCODING, SMI_PARAMETERS); 3688 CopyStringCharacters(var_right.value(), new_string, 3689 SmiConstant(Smi::kZero), left_length, right_length, 3690 String::TWO_BYTE_ENCODING, String::TWO_BYTE_ENCODING, 3691 SMI_PARAMETERS); 3692 result.Bind(new_string); 3693 Goto(&done_native); 3694 } 3695 3696 Bind(&slow); 3697 { 3698 // Try to unwrap indirect strings, restart the above attempt on success. 3699 MaybeDerefIndirectStrings(&var_left, left_instance_type, &var_right, 3700 right_instance_type, &non_cons); 3701 Goto(&runtime); 3702 } 3703 } 3704 Bind(&runtime); 3705 { 3706 result.Bind(CallRuntime(Runtime::kStringAdd, context, left, right)); 3707 Goto(&done); 3708 } 3709 3710 Bind(&done_native); 3711 { 3712 IncrementCounter(counters->string_add_native(), 1); 3713 Goto(&done); 3714 } 3715 3716 Bind(&done); 3717 return result.value(); 3718 } 3719 3720 Node* CodeStubAssembler::StringFromCodePoint(Node* codepoint, 3721 UnicodeEncoding encoding) { 3722 Variable var_result(this, MachineRepresentation::kTagged, 3723 EmptyStringConstant()); 3724 3725 Label if_isword16(this), if_isword32(this), return_result(this); 3726 3727 Branch(Uint32LessThan(codepoint, Int32Constant(0x10000)), &if_isword16, 3728 &if_isword32); 3729 3730 Bind(&if_isword16); 3731 { 3732 var_result.Bind(StringFromCharCode(codepoint)); 3733 Goto(&return_result); 3734 } 3735 3736 Bind(&if_isword32); 3737 { 3738 switch (encoding) { 3739 case UnicodeEncoding::UTF16: 3740 break; 3741 case UnicodeEncoding::UTF32: { 3742 // Convert UTF32 to UTF16 code units, and store as a 32 bit word. 3743 Node* lead_offset = Int32Constant(0xD800 - (0x10000 >> 10)); 3744 3745 // lead = (codepoint >> 10) + LEAD_OFFSET 3746 Node* lead = 3747 Int32Add(WordShr(codepoint, Int32Constant(10)), lead_offset); 3748 3749 // trail = (codepoint & 0x3FF) + 0xDC00; 3750 Node* trail = Int32Add(Word32And(codepoint, Int32Constant(0x3FF)), 3751 Int32Constant(0xDC00)); 3752 3753 // codpoint = (trail << 16) | lead; 3754 codepoint = Word32Or(WordShl(trail, Int32Constant(16)), lead); 3755 break; 3756 } 3757 } 3758 3759 Node* value = AllocateSeqTwoByteString(2); 3760 StoreNoWriteBarrier( 3761 MachineRepresentation::kWord32, value, 3762 IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag), 3763 codepoint); 3764 var_result.Bind(value); 3765 Goto(&return_result); 3766 } 3767 3768 Bind(&return_result); 3769 return var_result.value(); 3770 } 3771 3772 Node* CodeStubAssembler::StringToNumber(Node* context, Node* input) { 3773 Label runtime(this, Label::kDeferred); 3774 Label end(this); 3775 3776 Variable var_result(this, MachineRepresentation::kTagged); 3777 3778 // Check if string has a cached array index. 3779 Node* hash = LoadNameHashField(input); 3780 Node* bit = 3781 Word32And(hash, Int32Constant(String::kContainsCachedArrayIndexMask)); 3782 GotoIf(Word32NotEqual(bit, Int32Constant(0)), &runtime); 3783 3784 var_result.Bind( 3785 SmiTag(DecodeWordFromWord32<String::ArrayIndexValueBits>(hash))); 3786 Goto(&end); 3787 3788 Bind(&runtime); 3789 { 3790 var_result.Bind(CallRuntime(Runtime::kStringToNumber, context, input)); 3791 Goto(&end); 3792 } 3793 3794 Bind(&end); 3795 return var_result.value(); 3796 } 3797 3798 Node* CodeStubAssembler::NumberToString(Node* context, Node* argument) { 3799 Variable result(this, MachineRepresentation::kTagged); 3800 Label runtime(this, Label::kDeferred); 3801 Label smi(this); 3802 Label done(this, &result); 3803 3804 // Load the number string cache. 3805 Node* number_string_cache = LoadRoot(Heap::kNumberStringCacheRootIndex); 3806 3807 // Make the hash mask from the length of the number string cache. It 3808 // contains two elements (number and string) for each cache entry. 3809 // TODO(ishell): cleanup mask handling. 3810 Node* mask = 3811 BitcastTaggedToWord(LoadFixedArrayBaseLength(number_string_cache)); 3812 Node* one = IntPtrConstant(1); 3813 mask = IntPtrSub(mask, one); 3814 3815 GotoIf(TaggedIsSmi(argument), &smi); 3816 3817 // Argument isn't smi, check to see if it's a heap-number. 3818 Node* map = LoadMap(argument); 3819 GotoIfNot(IsHeapNumberMap(map), &runtime); 3820 3821 // Make a hash from the two 32-bit values of the double. 3822 Node* low = 3823 LoadObjectField(argument, HeapNumber::kValueOffset, MachineType::Int32()); 3824 Node* high = LoadObjectField(argument, HeapNumber::kValueOffset + kIntSize, 3825 MachineType::Int32()); 3826 Node* hash = Word32Xor(low, high); 3827 hash = ChangeInt32ToIntPtr(hash); 3828 hash = WordShl(hash, one); 3829 Node* index = WordAnd(hash, SmiUntag(BitcastWordToTagged(mask))); 3830 3831 // Cache entry's key must be a heap number 3832 Node* number_key = LoadFixedArrayElement(number_string_cache, index); 3833 GotoIf(TaggedIsSmi(number_key), &runtime); 3834 map = LoadMap(number_key); 3835 GotoIfNot(IsHeapNumberMap(map), &runtime); 3836 3837 // Cache entry's key must match the heap number value we're looking for. 3838 Node* low_compare = LoadObjectField(number_key, HeapNumber::kValueOffset, 3839 MachineType::Int32()); 3840 Node* high_compare = LoadObjectField( 3841 number_key, HeapNumber::kValueOffset + kIntSize, MachineType::Int32()); 3842 GotoIfNot(Word32Equal(low, low_compare), &runtime); 3843 GotoIfNot(Word32Equal(high, high_compare), &runtime); 3844 3845 // Heap number match, return value from cache entry. 3846 IncrementCounter(isolate()->counters()->number_to_string_native(), 1); 3847 result.Bind(LoadFixedArrayElement(number_string_cache, index, kPointerSize)); 3848 Goto(&done); 3849 3850 Bind(&runtime); 3851 { 3852 // No cache entry, go to the runtime. 3853 result.Bind(CallRuntime(Runtime::kNumberToString, context, argument)); 3854 } 3855 Goto(&done); 3856 3857 Bind(&smi); 3858 { 3859 // Load the smi key, make sure it matches the smi we're looking for. 3860 Node* smi_index = BitcastWordToTagged( 3861 WordAnd(WordShl(BitcastTaggedToWord(argument), one), mask)); 3862 Node* smi_key = LoadFixedArrayElement(number_string_cache, smi_index, 0, 3863 SMI_PARAMETERS); 3864 GotoIf(WordNotEqual(smi_key, argument), &runtime); 3865 3866 // Smi match, return value from cache entry. 3867 IncrementCounter(isolate()->counters()->number_to_string_native(), 1); 3868 result.Bind(LoadFixedArrayElement(number_string_cache, smi_index, 3869 kPointerSize, SMI_PARAMETERS)); 3870 Goto(&done); 3871 } 3872 3873 Bind(&done); 3874 return result.value(); 3875 } 3876 3877 Node* CodeStubAssembler::ToName(Node* context, Node* value) { 3878 Label end(this); 3879 Variable var_result(this, MachineRepresentation::kTagged); 3880 3881 Label is_number(this); 3882 GotoIf(TaggedIsSmi(value), &is_number); 3883 3884 Label not_name(this); 3885 Node* value_instance_type = LoadInstanceType(value); 3886 STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE); 3887 GotoIf(Int32GreaterThan(value_instance_type, Int32Constant(LAST_NAME_TYPE)), 3888 ¬_name); 3889 3890 var_result.Bind(value); 3891 Goto(&end); 3892 3893 Bind(&is_number); 3894 { 3895 Callable callable = CodeFactory::NumberToString(isolate()); 3896 var_result.Bind(CallStub(callable, context, value)); 3897 Goto(&end); 3898 } 3899 3900 Bind(¬_name); 3901 { 3902 GotoIf(Word32Equal(value_instance_type, Int32Constant(HEAP_NUMBER_TYPE)), 3903 &is_number); 3904 3905 Label not_oddball(this); 3906 GotoIf(Word32NotEqual(value_instance_type, Int32Constant(ODDBALL_TYPE)), 3907 ¬_oddball); 3908 3909 var_result.Bind(LoadObjectField(value, Oddball::kToStringOffset)); 3910 Goto(&end); 3911 3912 Bind(¬_oddball); 3913 { 3914 var_result.Bind(CallRuntime(Runtime::kToName, context, value)); 3915 Goto(&end); 3916 } 3917 } 3918 3919 Bind(&end); 3920 return var_result.value(); 3921 } 3922 3923 Node* CodeStubAssembler::NonNumberToNumber(Node* context, Node* input) { 3924 // Assert input is a HeapObject (not smi or heap number) 3925 CSA_ASSERT(this, Word32BinaryNot(TaggedIsSmi(input))); 3926 CSA_ASSERT(this, Word32BinaryNot(IsHeapNumberMap(LoadMap(input)))); 3927 3928 // We might need to loop once here due to ToPrimitive conversions. 3929 Variable var_input(this, MachineRepresentation::kTagged, input); 3930 Variable var_result(this, MachineRepresentation::kTagged); 3931 Label loop(this, &var_input); 3932 Label end(this); 3933 Goto(&loop); 3934 Bind(&loop); 3935 { 3936 // Load the current {input} value (known to be a HeapObject). 3937 Node* input = var_input.value(); 3938 3939 // Dispatch on the {input} instance type. 3940 Node* input_instance_type = LoadInstanceType(input); 3941 Label if_inputisstring(this), if_inputisoddball(this), 3942 if_inputisreceiver(this, Label::kDeferred), 3943 if_inputisother(this, Label::kDeferred); 3944 GotoIf(IsStringInstanceType(input_instance_type), &if_inputisstring); 3945 GotoIf(Word32Equal(input_instance_type, Int32Constant(ODDBALL_TYPE)), 3946 &if_inputisoddball); 3947 Branch(IsJSReceiverInstanceType(input_instance_type), &if_inputisreceiver, 3948 &if_inputisother); 3949 3950 Bind(&if_inputisstring); 3951 { 3952 // The {input} is a String, use the fast stub to convert it to a Number. 3953 var_result.Bind(StringToNumber(context, input)); 3954 Goto(&end); 3955 } 3956 3957 Bind(&if_inputisoddball); 3958 { 3959 // The {input} is an Oddball, we just need to load the Number value of it. 3960 var_result.Bind(LoadObjectField(input, Oddball::kToNumberOffset)); 3961 Goto(&end); 3962 } 3963 3964 Bind(&if_inputisreceiver); 3965 { 3966 // The {input} is a JSReceiver, we need to convert it to a Primitive first 3967 // using the ToPrimitive type conversion, preferably yielding a Number. 3968 Callable callable = CodeFactory::NonPrimitiveToPrimitive( 3969 isolate(), ToPrimitiveHint::kNumber); 3970 Node* result = CallStub(callable, context, input); 3971 3972 // Check if the {result} is already a Number. 3973 Label if_resultisnumber(this), if_resultisnotnumber(this); 3974 GotoIf(TaggedIsSmi(result), &if_resultisnumber); 3975 Node* result_map = LoadMap(result); 3976 Branch(IsHeapNumberMap(result_map), &if_resultisnumber, 3977 &if_resultisnotnumber); 3978 3979 Bind(&if_resultisnumber); 3980 { 3981 // The ToPrimitive conversion already gave us a Number, so we're done. 3982 var_result.Bind(result); 3983 Goto(&end); 3984 } 3985 3986 Bind(&if_resultisnotnumber); 3987 { 3988 // We now have a Primitive {result}, but it's not yet a Number. 3989 var_input.Bind(result); 3990 Goto(&loop); 3991 } 3992 } 3993 3994 Bind(&if_inputisother); 3995 { 3996 // The {input} is something else (e.g. Symbol), let the runtime figure 3997 // out the correct exception. 3998 // Note: We cannot tail call to the runtime here, as js-to-wasm 3999 // trampolines also use this code currently, and they declare all 4000 // outgoing parameters as untagged, while we would push a tagged 4001 // object here. 4002 var_result.Bind(CallRuntime(Runtime::kToNumber, context, input)); 4003 Goto(&end); 4004 } 4005 } 4006 4007 Bind(&end); 4008 return var_result.value(); 4009 } 4010 4011 Node* CodeStubAssembler::ToNumber(Node* context, Node* input) { 4012 Variable var_result(this, MachineRepresentation::kTagged); 4013 Label end(this); 4014 4015 Label not_smi(this, Label::kDeferred); 4016 GotoIfNot(TaggedIsSmi(input), ¬_smi); 4017 var_result.Bind(input); 4018 Goto(&end); 4019 4020 Bind(¬_smi); 4021 { 4022 Label not_heap_number(this, Label::kDeferred); 4023 Node* input_map = LoadMap(input); 4024 GotoIfNot(IsHeapNumberMap(input_map), ¬_heap_number); 4025 4026 var_result.Bind(input); 4027 Goto(&end); 4028 4029 Bind(¬_heap_number); 4030 { 4031 var_result.Bind(NonNumberToNumber(context, input)); 4032 Goto(&end); 4033 } 4034 } 4035 4036 Bind(&end); 4037 return var_result.value(); 4038 } 4039 4040 Node* CodeStubAssembler::ToUint32(Node* context, Node* input) { 4041 Node* const float_zero = Float64Constant(0.0); 4042 Node* const float_two_32 = Float64Constant(static_cast<double>(1ULL << 32)); 4043 4044 Label out(this); 4045 4046 Variable var_result(this, MachineRepresentation::kTagged, input); 4047 4048 // Early exit for positive smis. 4049 { 4050 // TODO(jgruber): This branch and the recheck below can be removed once we 4051 // have a ToNumber with multiple exits. 4052 Label next(this, Label::kDeferred); 4053 Branch(TaggedIsPositiveSmi(input), &out, &next); 4054 Bind(&next); 4055 } 4056 4057 Node* const number = ToNumber(context, input); 4058 var_result.Bind(number); 4059 4060 // Perhaps we have a positive smi now. 4061 { 4062 Label next(this, Label::kDeferred); 4063 Branch(TaggedIsPositiveSmi(number), &out, &next); 4064 Bind(&next); 4065 } 4066 4067 Label if_isnegativesmi(this), if_isheapnumber(this); 4068 Branch(TaggedIsSmi(number), &if_isnegativesmi, &if_isheapnumber); 4069 4070 Bind(&if_isnegativesmi); 4071 { 4072 // floor({input}) mod 2^32 === {input} + 2^32. 4073 Node* const float_number = SmiToFloat64(number); 4074 Node* const float_result = Float64Add(float_number, float_two_32); 4075 Node* const result = ChangeFloat64ToTagged(float_result); 4076 var_result.Bind(result); 4077 Goto(&out); 4078 } 4079 4080 Bind(&if_isheapnumber); 4081 { 4082 Label return_zero(this); 4083 Node* const value = LoadHeapNumberValue(number); 4084 4085 { 4086 // +-0. 4087 Label next(this); 4088 Branch(Float64Equal(value, float_zero), &return_zero, &next); 4089 Bind(&next); 4090 } 4091 4092 { 4093 // NaN. 4094 Label next(this); 4095 Branch(Float64Equal(value, value), &next, &return_zero); 4096 Bind(&next); 4097 } 4098 4099 { 4100 // +Infinity. 4101 Label next(this); 4102 Node* const positive_infinity = 4103 Float64Constant(std::numeric_limits<double>::infinity()); 4104 Branch(Float64Equal(value, positive_infinity), &return_zero, &next); 4105 Bind(&next); 4106 } 4107 4108 { 4109 // -Infinity. 4110 Label next(this); 4111 Node* const negative_infinity = 4112 Float64Constant(-1.0 * std::numeric_limits<double>::infinity()); 4113 Branch(Float64Equal(value, negative_infinity), &return_zero, &next); 4114 Bind(&next); 4115 } 4116 4117 // Return floor({input}) mod 2^32 (assuming mod semantics that always return 4118 // positive results). 4119 { 4120 Node* x = Float64Floor(value); 4121 x = Float64Mod(x, float_two_32); 4122 x = Float64Add(x, float_two_32); 4123 x = Float64Mod(x, float_two_32); 4124 4125 Node* const result = ChangeFloat64ToTagged(x); 4126 var_result.Bind(result); 4127 Goto(&out); 4128 } 4129 4130 Bind(&return_zero); 4131 { 4132 var_result.Bind(SmiConstant(Smi::kZero)); 4133 Goto(&out); 4134 } 4135 } 4136 4137 Bind(&out); 4138 return var_result.value(); 4139 } 4140 4141 Node* CodeStubAssembler::ToString(Node* context, Node* input) { 4142 Label is_number(this); 4143 Label runtime(this, Label::kDeferred); 4144 Variable result(this, MachineRepresentation::kTagged); 4145 Label done(this, &result); 4146 4147 GotoIf(TaggedIsSmi(input), &is_number); 4148 4149 Node* input_map = LoadMap(input); 4150 Node* input_instance_type = LoadMapInstanceType(input_map); 4151 4152 result.Bind(input); 4153 GotoIf(IsStringInstanceType(input_instance_type), &done); 4154 4155 Label not_heap_number(this); 4156 Branch(IsHeapNumberMap(input_map), &is_number, ¬_heap_number); 4157 4158 Bind(&is_number); 4159 result.Bind(NumberToString(context, input)); 4160 Goto(&done); 4161 4162 Bind(¬_heap_number); 4163 { 4164 GotoIf(Word32NotEqual(input_instance_type, Int32Constant(ODDBALL_TYPE)), 4165 &runtime); 4166 result.Bind(LoadObjectField(input, Oddball::kToStringOffset)); 4167 Goto(&done); 4168 } 4169 4170 Bind(&runtime); 4171 { 4172 result.Bind(CallRuntime(Runtime::kToString, context, input)); 4173 Goto(&done); 4174 } 4175 4176 Bind(&done); 4177 return result.value(); 4178 } 4179 4180 Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) { 4181 Label if_isreceiver(this, Label::kDeferred), if_isnotreceiver(this); 4182 Variable result(this, MachineRepresentation::kTagged); 4183 Label done(this, &result); 4184 4185 BranchIfJSReceiver(input, &if_isreceiver, &if_isnotreceiver); 4186 4187 Bind(&if_isreceiver); 4188 { 4189 // Convert {input} to a primitive first passing Number hint. 4190 Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate()); 4191 result.Bind(CallStub(callable, context, input)); 4192 Goto(&done); 4193 } 4194 4195 Bind(&if_isnotreceiver); 4196 { 4197 result.Bind(input); 4198 Goto(&done); 4199 } 4200 4201 Bind(&done); 4202 return result.value(); 4203 } 4204 4205 Node* CodeStubAssembler::ToInteger(Node* context, Node* input, 4206 ToIntegerTruncationMode mode) { 4207 // We might need to loop once for ToNumber conversion. 4208 Variable var_arg(this, MachineRepresentation::kTagged, input); 4209 Label loop(this, &var_arg), out(this); 4210 Goto(&loop); 4211 Bind(&loop); 4212 { 4213 // Shared entry points. 4214 Label return_zero(this, Label::kDeferred); 4215 4216 // Load the current {arg} value. 4217 Node* arg = var_arg.value(); 4218 4219 // Check if {arg} is a Smi. 4220 GotoIf(TaggedIsSmi(arg), &out); 4221 4222 // Check if {arg} is a HeapNumber. 4223 Label if_argisheapnumber(this), 4224 if_argisnotheapnumber(this, Label::kDeferred); 4225 Branch(IsHeapNumberMap(LoadMap(arg)), &if_argisheapnumber, 4226 &if_argisnotheapnumber); 4227 4228 Bind(&if_argisheapnumber); 4229 { 4230 // Load the floating-point value of {arg}. 4231 Node* arg_value = LoadHeapNumberValue(arg); 4232 4233 // Check if {arg} is NaN. 4234 GotoIfNot(Float64Equal(arg_value, arg_value), &return_zero); 4235 4236 // Truncate {arg} towards zero. 4237 Node* value = Float64Trunc(arg_value); 4238 4239 if (mode == kTruncateMinusZero) { 4240 // Truncate -0.0 to 0. 4241 GotoIf(Float64Equal(value, Float64Constant(0.0)), &return_zero); 4242 } 4243 4244 var_arg.Bind(ChangeFloat64ToTagged(value)); 4245 Goto(&out); 4246 } 4247 4248 Bind(&if_argisnotheapnumber); 4249 { 4250 // Need to convert {arg} to a Number first. 4251 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 4252 var_arg.Bind(CallStub(callable, context, arg)); 4253 Goto(&loop); 4254 } 4255 4256 Bind(&return_zero); 4257 var_arg.Bind(SmiConstant(Smi::kZero)); 4258 Goto(&out); 4259 } 4260 4261 Bind(&out); 4262 return var_arg.value(); 4263 } 4264 4265 Node* CodeStubAssembler::DecodeWord32(Node* word32, uint32_t shift, 4266 uint32_t mask) { 4267 return Word32Shr(Word32And(word32, Int32Constant(mask)), 4268 static_cast<int>(shift)); 4269 } 4270 4271 Node* CodeStubAssembler::DecodeWord(Node* word, uint32_t shift, uint32_t mask) { 4272 return WordShr(WordAnd(word, IntPtrConstant(mask)), static_cast<int>(shift)); 4273 } 4274 4275 void CodeStubAssembler::SetCounter(StatsCounter* counter, int value) { 4276 if (FLAG_native_code_counters && counter->Enabled()) { 4277 Node* counter_address = ExternalConstant(ExternalReference(counter)); 4278 StoreNoWriteBarrier(MachineRepresentation::kWord32, counter_address, 4279 Int32Constant(value)); 4280 } 4281 } 4282 4283 void CodeStubAssembler::IncrementCounter(StatsCounter* counter, int delta) { 4284 DCHECK(delta > 0); 4285 if (FLAG_native_code_counters && counter->Enabled()) { 4286 Node* counter_address = ExternalConstant(ExternalReference(counter)); 4287 Node* value = Load(MachineType::Int32(), counter_address); 4288 value = Int32Add(value, Int32Constant(delta)); 4289 StoreNoWriteBarrier(MachineRepresentation::kWord32, counter_address, value); 4290 } 4291 } 4292 4293 void CodeStubAssembler::DecrementCounter(StatsCounter* counter, int delta) { 4294 DCHECK(delta > 0); 4295 if (FLAG_native_code_counters && counter->Enabled()) { 4296 Node* counter_address = ExternalConstant(ExternalReference(counter)); 4297 Node* value = Load(MachineType::Int32(), counter_address); 4298 value = Int32Sub(value, Int32Constant(delta)); 4299 StoreNoWriteBarrier(MachineRepresentation::kWord32, counter_address, value); 4300 } 4301 } 4302 4303 void CodeStubAssembler::Increment(Variable& variable, int value, 4304 ParameterMode mode) { 4305 DCHECK_IMPLIES(mode == INTPTR_PARAMETERS, 4306 variable.rep() == MachineType::PointerRepresentation()); 4307 DCHECK_IMPLIES(mode == SMI_PARAMETERS, 4308 variable.rep() == MachineRepresentation::kTagged || 4309 variable.rep() == MachineRepresentation::kTaggedSigned); 4310 variable.Bind( 4311 IntPtrOrSmiAdd(variable.value(), IntPtrOrSmiConstant(value, mode), mode)); 4312 } 4313 4314 void CodeStubAssembler::Use(Label* label) { 4315 GotoIf(Word32Equal(Int32Constant(0), Int32Constant(1)), label); 4316 } 4317 4318 void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex, 4319 Variable* var_index, Label* if_keyisunique, 4320 Variable* var_unique, Label* if_bailout) { 4321 DCHECK_EQ(MachineType::PointerRepresentation(), var_index->rep()); 4322 DCHECK_EQ(MachineRepresentation::kTagged, var_unique->rep()); 4323 Comment("TryToName"); 4324 4325 Label if_hascachedindex(this), if_keyisnotindex(this), if_thinstring(this); 4326 // Handle Smi and HeapNumber keys. 4327 var_index->Bind(TryToIntptr(key, &if_keyisnotindex)); 4328 Goto(if_keyisindex); 4329 4330 Bind(&if_keyisnotindex); 4331 Node* key_map = LoadMap(key); 4332 var_unique->Bind(key); 4333 // Symbols are unique. 4334 GotoIf(IsSymbolMap(key_map), if_keyisunique); 4335 Node* key_instance_type = LoadMapInstanceType(key_map); 4336 // Miss if |key| is not a String. 4337 STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE); 4338 GotoIfNot(IsStringInstanceType(key_instance_type), if_bailout); 4339 // |key| is a String. Check if it has a cached array index. 4340 Node* hash = LoadNameHashField(key); 4341 Node* contains_index = 4342 Word32And(hash, Int32Constant(Name::kContainsCachedArrayIndexMask)); 4343 GotoIf(Word32Equal(contains_index, Int32Constant(0)), &if_hascachedindex); 4344 // No cached array index. If the string knows that it contains an index, 4345 // then it must be an uncacheable index. Handle this case in the runtime. 4346 Node* not_an_index = 4347 Word32And(hash, Int32Constant(Name::kIsNotArrayIndexMask)); 4348 GotoIf(Word32Equal(not_an_index, Int32Constant(0)), if_bailout); 4349 // Check if we have a ThinString. 4350 GotoIf(Word32Equal(key_instance_type, Int32Constant(THIN_STRING_TYPE)), 4351 &if_thinstring); 4352 GotoIf( 4353 Word32Equal(key_instance_type, Int32Constant(THIN_ONE_BYTE_STRING_TYPE)), 4354 &if_thinstring); 4355 // Finally, check if |key| is internalized. 4356 STATIC_ASSERT(kNotInternalizedTag != 0); 4357 Node* not_internalized = 4358 Word32And(key_instance_type, Int32Constant(kIsNotInternalizedMask)); 4359 GotoIf(Word32NotEqual(not_internalized, Int32Constant(0)), if_bailout); 4360 Goto(if_keyisunique); 4361 4362 Bind(&if_thinstring); 4363 var_unique->Bind(LoadObjectField(key, ThinString::kActualOffset)); 4364 Goto(if_keyisunique); 4365 4366 Bind(&if_hascachedindex); 4367 var_index->Bind(DecodeWordFromWord32<Name::ArrayIndexValueBits>(hash)); 4368 Goto(if_keyisindex); 4369 } 4370 4371 template <typename Dictionary> 4372 Node* CodeStubAssembler::EntryToIndex(Node* entry, int field_index) { 4373 Node* entry_index = IntPtrMul(entry, IntPtrConstant(Dictionary::kEntrySize)); 4374 return IntPtrAdd(entry_index, IntPtrConstant(Dictionary::kElementsStartIndex + 4375 field_index)); 4376 } 4377 4378 template Node* CodeStubAssembler::EntryToIndex<NameDictionary>(Node*, int); 4379 template Node* CodeStubAssembler::EntryToIndex<GlobalDictionary>(Node*, int); 4380 template Node* CodeStubAssembler::EntryToIndex<SeededNumberDictionary>(Node*, 4381 int); 4382 4383 Node* CodeStubAssembler::HashTableComputeCapacity(Node* at_least_space_for) { 4384 Node* capacity = IntPtrRoundUpToPowerOfTwo32( 4385 WordShl(at_least_space_for, IntPtrConstant(1))); 4386 return IntPtrMax(capacity, IntPtrConstant(HashTableBase::kMinCapacity)); 4387 } 4388 4389 Node* CodeStubAssembler::IntPtrMax(Node* left, Node* right) { 4390 return SelectConstant(IntPtrGreaterThanOrEqual(left, right), left, right, 4391 MachineType::PointerRepresentation()); 4392 } 4393 4394 Node* CodeStubAssembler::IntPtrMin(Node* left, Node* right) { 4395 return SelectConstant(IntPtrLessThanOrEqual(left, right), left, right, 4396 MachineType::PointerRepresentation()); 4397 } 4398 4399 template <class Dictionary> 4400 Node* CodeStubAssembler::GetNumberOfElements(Node* dictionary) { 4401 return LoadFixedArrayElement(dictionary, Dictionary::kNumberOfElementsIndex); 4402 } 4403 4404 template <class Dictionary> 4405 void CodeStubAssembler::SetNumberOfElements(Node* dictionary, 4406 Node* num_elements_smi) { 4407 StoreFixedArrayElement(dictionary, Dictionary::kNumberOfElementsIndex, 4408 num_elements_smi, SKIP_WRITE_BARRIER); 4409 } 4410 4411 template <class Dictionary> 4412 Node* CodeStubAssembler::GetNumberOfDeletedElements(Node* dictionary) { 4413 return LoadFixedArrayElement(dictionary, 4414 Dictionary::kNumberOfDeletedElementsIndex); 4415 } 4416 4417 template <class Dictionary> 4418 Node* CodeStubAssembler::GetCapacity(Node* dictionary) { 4419 return LoadFixedArrayElement(dictionary, Dictionary::kCapacityIndex); 4420 } 4421 4422 template <class Dictionary> 4423 Node* CodeStubAssembler::GetNextEnumerationIndex(Node* dictionary) { 4424 return LoadFixedArrayElement(dictionary, 4425 Dictionary::kNextEnumerationIndexIndex); 4426 } 4427 4428 template <class Dictionary> 4429 void CodeStubAssembler::SetNextEnumerationIndex(Node* dictionary, 4430 Node* next_enum_index_smi) { 4431 StoreFixedArrayElement(dictionary, Dictionary::kNextEnumerationIndexIndex, 4432 next_enum_index_smi, SKIP_WRITE_BARRIER); 4433 } 4434 4435 template <typename Dictionary> 4436 void CodeStubAssembler::NameDictionaryLookup(Node* dictionary, 4437 Node* unique_name, Label* if_found, 4438 Variable* var_name_index, 4439 Label* if_not_found, 4440 int inlined_probes, 4441 LookupMode mode) { 4442 CSA_ASSERT(this, IsDictionary(dictionary)); 4443 DCHECK_EQ(MachineType::PointerRepresentation(), var_name_index->rep()); 4444 DCHECK_IMPLIES(mode == kFindInsertionIndex, 4445 inlined_probes == 0 && if_found == nullptr); 4446 Comment("NameDictionaryLookup"); 4447 4448 Node* capacity = SmiUntag(GetCapacity<Dictionary>(dictionary)); 4449 Node* mask = IntPtrSub(capacity, IntPtrConstant(1)); 4450 Node* hash = ChangeUint32ToWord(LoadNameHash(unique_name)); 4451 4452 // See Dictionary::FirstProbe(). 4453 Node* count = IntPtrConstant(0); 4454 Node* entry = WordAnd(hash, mask); 4455 4456 for (int i = 0; i < inlined_probes; i++) { 4457 Node* index = EntryToIndex<Dictionary>(entry); 4458 var_name_index->Bind(index); 4459 4460 Node* current = LoadFixedArrayElement(dictionary, index); 4461 GotoIf(WordEqual(current, unique_name), if_found); 4462 4463 // See Dictionary::NextProbe(). 4464 count = IntPtrConstant(i + 1); 4465 entry = WordAnd(IntPtrAdd(entry, count), mask); 4466 } 4467 if (mode == kFindInsertionIndex) { 4468 // Appease the variable merging algorithm for "Goto(&loop)" below. 4469 var_name_index->Bind(IntPtrConstant(0)); 4470 } 4471 4472 Node* undefined = UndefinedConstant(); 4473 Node* the_hole = mode == kFindExisting ? nullptr : TheHoleConstant(); 4474 4475 Variable var_count(this, MachineType::PointerRepresentation(), count); 4476 Variable var_entry(this, MachineType::PointerRepresentation(), entry); 4477 Variable* loop_vars[] = {&var_count, &var_entry, var_name_index}; 4478 Label loop(this, 3, loop_vars); 4479 Goto(&loop); 4480 Bind(&loop); 4481 { 4482 Node* entry = var_entry.value(); 4483 4484 Node* index = EntryToIndex<Dictionary>(entry); 4485 var_name_index->Bind(index); 4486 4487 Node* current = LoadFixedArrayElement(dictionary, index); 4488 GotoIf(WordEqual(current, undefined), if_not_found); 4489 if (mode == kFindExisting) { 4490 GotoIf(WordEqual(current, unique_name), if_found); 4491 } else { 4492 DCHECK_EQ(kFindInsertionIndex, mode); 4493 GotoIf(WordEqual(current, the_hole), if_not_found); 4494 } 4495 4496 // See Dictionary::NextProbe(). 4497 Increment(var_count); 4498 entry = WordAnd(IntPtrAdd(entry, var_count.value()), mask); 4499 4500 var_entry.Bind(entry); 4501 Goto(&loop); 4502 } 4503 } 4504 4505 // Instantiate template methods to workaround GCC compilation issue. 4506 template void CodeStubAssembler::NameDictionaryLookup<NameDictionary>( 4507 Node*, Node*, Label*, Variable*, Label*, int, LookupMode); 4508 template void CodeStubAssembler::NameDictionaryLookup<GlobalDictionary>( 4509 Node*, Node*, Label*, Variable*, Label*, int, LookupMode); 4510 4511 Node* CodeStubAssembler::ComputeIntegerHash(Node* key, Node* seed) { 4512 // See v8::internal::ComputeIntegerHash() 4513 Node* hash = TruncateWordToWord32(key); 4514 hash = Word32Xor(hash, seed); 4515 hash = Int32Add(Word32Xor(hash, Int32Constant(0xffffffff)), 4516 Word32Shl(hash, Int32Constant(15))); 4517 hash = Word32Xor(hash, Word32Shr(hash, Int32Constant(12))); 4518 hash = Int32Add(hash, Word32Shl(hash, Int32Constant(2))); 4519 hash = Word32Xor(hash, Word32Shr(hash, Int32Constant(4))); 4520 hash = Int32Mul(hash, Int32Constant(2057)); 4521 hash = Word32Xor(hash, Word32Shr(hash, Int32Constant(16))); 4522 return Word32And(hash, Int32Constant(0x3fffffff)); 4523 } 4524 4525 template <typename Dictionary> 4526 void CodeStubAssembler::NumberDictionaryLookup(Node* dictionary, 4527 Node* intptr_index, 4528 Label* if_found, 4529 Variable* var_entry, 4530 Label* if_not_found) { 4531 CSA_ASSERT(this, IsDictionary(dictionary)); 4532 DCHECK_EQ(MachineType::PointerRepresentation(), var_entry->rep()); 4533 Comment("NumberDictionaryLookup"); 4534 4535 Node* capacity = SmiUntag(GetCapacity<Dictionary>(dictionary)); 4536 Node* mask = IntPtrSub(capacity, IntPtrConstant(1)); 4537 4538 Node* int32_seed; 4539 if (Dictionary::ShapeT::UsesSeed) { 4540 int32_seed = HashSeed(); 4541 } else { 4542 int32_seed = Int32Constant(kZeroHashSeed); 4543 } 4544 Node* hash = ChangeUint32ToWord(ComputeIntegerHash(intptr_index, int32_seed)); 4545 Node* key_as_float64 = RoundIntPtrToFloat64(intptr_index); 4546 4547 // See Dictionary::FirstProbe(). 4548 Node* count = IntPtrConstant(0); 4549 Node* entry = WordAnd(hash, mask); 4550 4551 Node* undefined = UndefinedConstant(); 4552 Node* the_hole = TheHoleConstant(); 4553 4554 Variable var_count(this, MachineType::PointerRepresentation(), count); 4555 Variable* loop_vars[] = {&var_count, var_entry}; 4556 Label loop(this, 2, loop_vars); 4557 var_entry->Bind(entry); 4558 Goto(&loop); 4559 Bind(&loop); 4560 { 4561 Node* entry = var_entry->value(); 4562 4563 Node* index = EntryToIndex<Dictionary>(entry); 4564 Node* current = LoadFixedArrayElement(dictionary, index); 4565 GotoIf(WordEqual(current, undefined), if_not_found); 4566 Label next_probe(this); 4567 { 4568 Label if_currentissmi(this), if_currentisnotsmi(this); 4569 Branch(TaggedIsSmi(current), &if_currentissmi, &if_currentisnotsmi); 4570 Bind(&if_currentissmi); 4571 { 4572 Node* current_value = SmiUntag(current); 4573 Branch(WordEqual(current_value, intptr_index), if_found, &next_probe); 4574 } 4575 Bind(&if_currentisnotsmi); 4576 { 4577 GotoIf(WordEqual(current, the_hole), &next_probe); 4578 // Current must be the Number. 4579 Node* current_value = LoadHeapNumberValue(current); 4580 Branch(Float64Equal(current_value, key_as_float64), if_found, 4581 &next_probe); 4582 } 4583 } 4584 4585 Bind(&next_probe); 4586 // See Dictionary::NextProbe(). 4587 Increment(var_count); 4588 entry = WordAnd(IntPtrAdd(entry, var_count.value()), mask); 4589 4590 var_entry->Bind(entry); 4591 Goto(&loop); 4592 } 4593 } 4594 4595 template <class Dictionary> 4596 void CodeStubAssembler::FindInsertionEntry(Node* dictionary, Node* key, 4597 Variable* var_key_index) { 4598 UNREACHABLE(); 4599 } 4600 4601 template <> 4602 void CodeStubAssembler::FindInsertionEntry<NameDictionary>( 4603 Node* dictionary, Node* key, Variable* var_key_index) { 4604 Label done(this); 4605 NameDictionaryLookup<NameDictionary>(dictionary, key, nullptr, var_key_index, 4606 &done, 0, kFindInsertionIndex); 4607 Bind(&done); 4608 } 4609 4610 template <class Dictionary> 4611 void CodeStubAssembler::InsertEntry(Node* dictionary, Node* key, Node* value, 4612 Node* index, Node* enum_index) { 4613 UNREACHABLE(); // Use specializations instead. 4614 } 4615 4616 template <> 4617 void CodeStubAssembler::InsertEntry<NameDictionary>(Node* dictionary, 4618 Node* name, Node* value, 4619 Node* index, 4620 Node* enum_index) { 4621 // Store name and value. 4622 StoreFixedArrayElement(dictionary, index, name); 4623 StoreValueByKeyIndex<NameDictionary>(dictionary, index, value); 4624 4625 // Prepare details of the new property. 4626 const int kInitialIndex = 0; 4627 PropertyDetails d(kData, NONE, kInitialIndex, PropertyCellType::kNoCell); 4628 enum_index = 4629 SmiShl(enum_index, PropertyDetails::DictionaryStorageField::kShift); 4630 STATIC_ASSERT(kInitialIndex == 0); 4631 Variable var_details(this, MachineRepresentation::kTaggedSigned, 4632 SmiOr(SmiConstant(d.AsSmi()), enum_index)); 4633 4634 // Private names must be marked non-enumerable. 4635 Label not_private(this, &var_details); 4636 GotoIfNot(IsSymbolMap(LoadMap(name)), ¬_private); 4637 Node* flags = SmiToWord32(LoadObjectField(name, Symbol::kFlagsOffset)); 4638 const int kPrivateMask = 1 << Symbol::kPrivateBit; 4639 GotoIfNot(IsSetWord32(flags, kPrivateMask), ¬_private); 4640 Node* dont_enum = 4641 SmiShl(SmiConstant(DONT_ENUM), PropertyDetails::AttributesField::kShift); 4642 var_details.Bind(SmiOr(var_details.value(), dont_enum)); 4643 Goto(¬_private); 4644 Bind(¬_private); 4645 4646 // Finally, store the details. 4647 StoreDetailsByKeyIndex<NameDictionary>(dictionary, index, 4648 var_details.value()); 4649 } 4650 4651 template <> 4652 void CodeStubAssembler::InsertEntry<GlobalDictionary>(Node* dictionary, 4653 Node* key, Node* value, 4654 Node* index, 4655 Node* enum_index) { 4656 UNIMPLEMENTED(); 4657 } 4658 4659 template <class Dictionary> 4660 void CodeStubAssembler::Add(Node* dictionary, Node* key, Node* value, 4661 Label* bailout) { 4662 Node* capacity = GetCapacity<Dictionary>(dictionary); 4663 Node* nof = GetNumberOfElements<Dictionary>(dictionary); 4664 Node* new_nof = SmiAdd(nof, SmiConstant(1)); 4665 // Require 33% to still be free after adding additional_elements. 4666 // Computing "x + (x >> 1)" on a Smi x does not return a valid Smi! 4667 // But that's OK here because it's only used for a comparison. 4668 Node* required_capacity_pseudo_smi = SmiAdd(new_nof, SmiShr(new_nof, 1)); 4669 GotoIf(SmiBelow(capacity, required_capacity_pseudo_smi), bailout); 4670 // Require rehashing if more than 50% of free elements are deleted elements. 4671 Node* deleted = GetNumberOfDeletedElements<Dictionary>(dictionary); 4672 CSA_ASSERT(this, SmiAbove(capacity, new_nof)); 4673 Node* half_of_free_elements = SmiShr(SmiSub(capacity, new_nof), 1); 4674 GotoIf(SmiAbove(deleted, half_of_free_elements), bailout); 4675 Node* enum_index = nullptr; 4676 if (Dictionary::kIsEnumerable) { 4677 enum_index = GetNextEnumerationIndex<Dictionary>(dictionary); 4678 Node* new_enum_index = SmiAdd(enum_index, SmiConstant(1)); 4679 Node* max_enum_index = 4680 SmiConstant(PropertyDetails::DictionaryStorageField::kMax); 4681 GotoIf(SmiAbove(new_enum_index, max_enum_index), bailout); 4682 4683 // No more bailouts after this point. 4684 // Operations from here on can have side effects. 4685 4686 SetNextEnumerationIndex<Dictionary>(dictionary, new_enum_index); 4687 } else { 4688 USE(enum_index); 4689 } 4690 SetNumberOfElements<Dictionary>(dictionary, new_nof); 4691 4692 Variable var_key_index(this, MachineType::PointerRepresentation()); 4693 FindInsertionEntry<Dictionary>(dictionary, key, &var_key_index); 4694 InsertEntry<Dictionary>(dictionary, key, value, var_key_index.value(), 4695 enum_index); 4696 } 4697 4698 template void CodeStubAssembler::Add<NameDictionary>(Node*, Node*, Node*, 4699 Label*); 4700 4701 void CodeStubAssembler::DescriptorLookupLinear(Node* unique_name, 4702 Node* descriptors, Node* nof, 4703 Label* if_found, 4704 Variable* var_name_index, 4705 Label* if_not_found) { 4706 Comment("DescriptorLookupLinear"); 4707 Node* first_inclusive = IntPtrConstant(DescriptorArray::ToKeyIndex(0)); 4708 Node* factor = IntPtrConstant(DescriptorArray::kEntrySize); 4709 Node* last_exclusive = IntPtrAdd(first_inclusive, IntPtrMul(nof, factor)); 4710 4711 BuildFastLoop(last_exclusive, first_inclusive, 4712 [this, descriptors, unique_name, if_found, 4713 var_name_index](Node* name_index) { 4714 Node* candidate_name = 4715 LoadFixedArrayElement(descriptors, name_index); 4716 var_name_index->Bind(name_index); 4717 GotoIf(WordEqual(candidate_name, unique_name), if_found); 4718 }, 4719 -DescriptorArray::kEntrySize, INTPTR_PARAMETERS, 4720 IndexAdvanceMode::kPre); 4721 Goto(if_not_found); 4722 } 4723 4724 Node* CodeStubAssembler::DescriptorArrayNumberOfEntries(Node* descriptors) { 4725 return LoadAndUntagToWord32FixedArrayElement( 4726 descriptors, IntPtrConstant(DescriptorArray::kDescriptorLengthIndex)); 4727 } 4728 4729 namespace { 4730 4731 Node* DescriptorNumberToIndex(CodeStubAssembler* a, Node* descriptor_number) { 4732 Node* descriptor_size = a->Int32Constant(DescriptorArray::kEntrySize); 4733 Node* index = a->Int32Mul(descriptor_number, descriptor_size); 4734 return a->ChangeInt32ToIntPtr(index); 4735 } 4736 4737 } // namespace 4738 4739 Node* CodeStubAssembler::DescriptorArrayToKeyIndex(Node* descriptor_number) { 4740 return IntPtrAdd(IntPtrConstant(DescriptorArray::ToKeyIndex(0)), 4741 DescriptorNumberToIndex(this, descriptor_number)); 4742 } 4743 4744 Node* CodeStubAssembler::DescriptorArrayGetSortedKeyIndex( 4745 Node* descriptors, Node* descriptor_number) { 4746 const int details_offset = DescriptorArray::ToDetailsIndex(0) * kPointerSize; 4747 Node* details = LoadAndUntagToWord32FixedArrayElement( 4748 descriptors, DescriptorNumberToIndex(this, descriptor_number), 4749 details_offset); 4750 return DecodeWord32<PropertyDetails::DescriptorPointer>(details); 4751 } 4752 4753 Node* CodeStubAssembler::DescriptorArrayGetKey(Node* descriptors, 4754 Node* descriptor_number) { 4755 const int key_offset = DescriptorArray::ToKeyIndex(0) * kPointerSize; 4756 return LoadFixedArrayElement(descriptors, 4757 DescriptorNumberToIndex(this, descriptor_number), 4758 key_offset); 4759 } 4760 4761 void CodeStubAssembler::DescriptorLookupBinary(Node* unique_name, 4762 Node* descriptors, Node* nof, 4763 Label* if_found, 4764 Variable* var_name_index, 4765 Label* if_not_found) { 4766 Comment("DescriptorLookupBinary"); 4767 Variable var_low(this, MachineRepresentation::kWord32, Int32Constant(0)); 4768 Node* limit = 4769 Int32Sub(DescriptorArrayNumberOfEntries(descriptors), Int32Constant(1)); 4770 Variable var_high(this, MachineRepresentation::kWord32, limit); 4771 Node* hash = LoadNameHashField(unique_name); 4772 CSA_ASSERT(this, Word32NotEqual(hash, Int32Constant(0))); 4773 4774 // Assume non-empty array. 4775 CSA_ASSERT(this, Uint32LessThanOrEqual(var_low.value(), var_high.value())); 4776 4777 Variable* loop_vars[] = {&var_high, &var_low}; 4778 Label binary_loop(this, 2, loop_vars); 4779 Goto(&binary_loop); 4780 Bind(&binary_loop); 4781 { 4782 // mid = low + (high - low) / 2 (to avoid overflow in "(low + high) / 2"). 4783 Node* mid = 4784 Int32Add(var_low.value(), 4785 Word32Shr(Int32Sub(var_high.value(), var_low.value()), 1)); 4786 // mid_name = descriptors->GetSortedKey(mid). 4787 Node* sorted_key_index = DescriptorArrayGetSortedKeyIndex(descriptors, mid); 4788 Node* mid_name = DescriptorArrayGetKey(descriptors, sorted_key_index); 4789 4790 Node* mid_hash = LoadNameHashField(mid_name); 4791 4792 Label mid_greater(this), mid_less(this), merge(this); 4793 Branch(Uint32GreaterThanOrEqual(mid_hash, hash), &mid_greater, &mid_less); 4794 Bind(&mid_greater); 4795 { 4796 var_high.Bind(mid); 4797 Goto(&merge); 4798 } 4799 Bind(&mid_less); 4800 { 4801 var_low.Bind(Int32Add(mid, Int32Constant(1))); 4802 Goto(&merge); 4803 } 4804 Bind(&merge); 4805 GotoIf(Word32NotEqual(var_low.value(), var_high.value()), &binary_loop); 4806 } 4807 4808 Label scan_loop(this, &var_low); 4809 Goto(&scan_loop); 4810 Bind(&scan_loop); 4811 { 4812 GotoIf(Int32GreaterThan(var_low.value(), limit), if_not_found); 4813 4814 Node* sort_index = 4815 DescriptorArrayGetSortedKeyIndex(descriptors, var_low.value()); 4816 Node* current_name = DescriptorArrayGetKey(descriptors, sort_index); 4817 Node* current_hash = LoadNameHashField(current_name); 4818 GotoIf(Word32NotEqual(current_hash, hash), if_not_found); 4819 Label next(this); 4820 GotoIf(WordNotEqual(current_name, unique_name), &next); 4821 GotoIf(Int32GreaterThanOrEqual(sort_index, nof), if_not_found); 4822 var_name_index->Bind(DescriptorArrayToKeyIndex(sort_index)); 4823 Goto(if_found); 4824 4825 Bind(&next); 4826 var_low.Bind(Int32Add(var_low.value(), Int32Constant(1))); 4827 Goto(&scan_loop); 4828 } 4829 } 4830 4831 void CodeStubAssembler::DescriptorLookup(Node* unique_name, Node* descriptors, 4832 Node* bitfield3, Label* if_found, 4833 Variable* var_name_index, 4834 Label* if_not_found) { 4835 Comment("DescriptorArrayLookup"); 4836 Node* nof = DecodeWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3); 4837 GotoIf(Word32Equal(nof, Int32Constant(0)), if_not_found); 4838 Label linear_search(this), binary_search(this); 4839 const int kMaxElementsForLinearSearch = 32; 4840 Branch(Int32LessThanOrEqual(nof, Int32Constant(kMaxElementsForLinearSearch)), 4841 &linear_search, &binary_search); 4842 Bind(&linear_search); 4843 { 4844 DescriptorLookupLinear(unique_name, descriptors, ChangeInt32ToIntPtr(nof), 4845 if_found, var_name_index, if_not_found); 4846 } 4847 Bind(&binary_search); 4848 { 4849 DescriptorLookupBinary(unique_name, descriptors, nof, if_found, 4850 var_name_index, if_not_found); 4851 } 4852 } 4853 4854 void CodeStubAssembler::TryLookupProperty( 4855 Node* object, Node* map, Node* instance_type, Node* unique_name, 4856 Label* if_found_fast, Label* if_found_dict, Label* if_found_global, 4857 Variable* var_meta_storage, Variable* var_name_index, Label* if_not_found, 4858 Label* if_bailout) { 4859 DCHECK_EQ(MachineRepresentation::kTagged, var_meta_storage->rep()); 4860 DCHECK_EQ(MachineType::PointerRepresentation(), var_name_index->rep()); 4861 4862 Label if_objectisspecial(this); 4863 STATIC_ASSERT(JS_GLOBAL_OBJECT_TYPE <= LAST_SPECIAL_RECEIVER_TYPE); 4864 GotoIf(Int32LessThanOrEqual(instance_type, 4865 Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)), 4866 &if_objectisspecial); 4867 4868 uint32_t mask = 4869 1 << Map::kHasNamedInterceptor | 1 << Map::kIsAccessCheckNeeded; 4870 CSA_ASSERT(this, Word32BinaryNot(IsSetWord32(LoadMapBitField(map), mask))); 4871 USE(mask); 4872 4873 Node* bit_field3 = LoadMapBitField3(map); 4874 Label if_isfastmap(this), if_isslowmap(this); 4875 Branch(IsSetWord32<Map::DictionaryMap>(bit_field3), &if_isslowmap, 4876 &if_isfastmap); 4877 Bind(&if_isfastmap); 4878 { 4879 Node* descriptors = LoadMapDescriptors(map); 4880 var_meta_storage->Bind(descriptors); 4881 4882 DescriptorLookup(unique_name, descriptors, bit_field3, if_found_fast, 4883 var_name_index, if_not_found); 4884 } 4885 Bind(&if_isslowmap); 4886 { 4887 Node* dictionary = LoadProperties(object); 4888 var_meta_storage->Bind(dictionary); 4889 4890 NameDictionaryLookup<NameDictionary>(dictionary, unique_name, if_found_dict, 4891 var_name_index, if_not_found); 4892 } 4893 Bind(&if_objectisspecial); 4894 { 4895 // Handle global object here and other special objects in runtime. 4896 GotoIfNot(Word32Equal(instance_type, Int32Constant(JS_GLOBAL_OBJECT_TYPE)), 4897 if_bailout); 4898 4899 // Handle interceptors and access checks in runtime. 4900 Node* bit_field = LoadMapBitField(map); 4901 Node* mask = Int32Constant(1 << Map::kHasNamedInterceptor | 4902 1 << Map::kIsAccessCheckNeeded); 4903 GotoIf(Word32NotEqual(Word32And(bit_field, mask), Int32Constant(0)), 4904 if_bailout); 4905 4906 Node* dictionary = LoadProperties(object); 4907 var_meta_storage->Bind(dictionary); 4908 4909 NameDictionaryLookup<GlobalDictionary>( 4910 dictionary, unique_name, if_found_global, var_name_index, if_not_found); 4911 } 4912 } 4913 4914 void CodeStubAssembler::TryHasOwnProperty(Node* object, Node* map, 4915 Node* instance_type, 4916 Node* unique_name, Label* if_found, 4917 Label* if_not_found, 4918 Label* if_bailout) { 4919 Comment("TryHasOwnProperty"); 4920 Variable var_meta_storage(this, MachineRepresentation::kTagged); 4921 Variable var_name_index(this, MachineType::PointerRepresentation()); 4922 4923 Label if_found_global(this); 4924 TryLookupProperty(object, map, instance_type, unique_name, if_found, if_found, 4925 &if_found_global, &var_meta_storage, &var_name_index, 4926 if_not_found, if_bailout); 4927 Bind(&if_found_global); 4928 { 4929 Variable var_value(this, MachineRepresentation::kTagged); 4930 Variable var_details(this, MachineRepresentation::kWord32); 4931 // Check if the property cell is not deleted. 4932 LoadPropertyFromGlobalDictionary(var_meta_storage.value(), 4933 var_name_index.value(), &var_value, 4934 &var_details, if_not_found); 4935 Goto(if_found); 4936 } 4937 } 4938 4939 void CodeStubAssembler::LoadPropertyFromFastObject(Node* object, Node* map, 4940 Node* descriptors, 4941 Node* name_index, 4942 Variable* var_details, 4943 Variable* var_value) { 4944 DCHECK_EQ(MachineRepresentation::kWord32, var_details->rep()); 4945 DCHECK_EQ(MachineRepresentation::kTagged, var_value->rep()); 4946 Comment("[ LoadPropertyFromFastObject"); 4947 4948 Node* details = 4949 LoadDetailsByKeyIndex<DescriptorArray>(descriptors, name_index); 4950 var_details->Bind(details); 4951 4952 Node* location = DecodeWord32<PropertyDetails::LocationField>(details); 4953 4954 Label if_in_field(this), if_in_descriptor(this), done(this); 4955 Branch(Word32Equal(location, Int32Constant(kField)), &if_in_field, 4956 &if_in_descriptor); 4957 Bind(&if_in_field); 4958 { 4959 Node* field_index = 4960 DecodeWordFromWord32<PropertyDetails::FieldIndexField>(details); 4961 Node* representation = 4962 DecodeWord32<PropertyDetails::RepresentationField>(details); 4963 4964 Node* inobject_properties = LoadMapInobjectProperties(map); 4965 4966 Label if_inobject(this), if_backing_store(this); 4967 Variable var_double_value(this, MachineRepresentation::kFloat64); 4968 Label rebox_double(this, &var_double_value); 4969 Branch(UintPtrLessThan(field_index, inobject_properties), &if_inobject, 4970 &if_backing_store); 4971 Bind(&if_inobject); 4972 { 4973 Comment("if_inobject"); 4974 Node* field_offset = 4975 IntPtrMul(IntPtrSub(LoadMapInstanceSize(map), 4976 IntPtrSub(inobject_properties, field_index)), 4977 IntPtrConstant(kPointerSize)); 4978 4979 Label if_double(this), if_tagged(this); 4980 Branch(Word32NotEqual(representation, 4981 Int32Constant(Representation::kDouble)), 4982 &if_tagged, &if_double); 4983 Bind(&if_tagged); 4984 { 4985 var_value->Bind(LoadObjectField(object, field_offset)); 4986 Goto(&done); 4987 } 4988 Bind(&if_double); 4989 { 4990 if (FLAG_unbox_double_fields) { 4991 var_double_value.Bind( 4992 LoadObjectField(object, field_offset, MachineType::Float64())); 4993 } else { 4994 Node* mutable_heap_number = LoadObjectField(object, field_offset); 4995 var_double_value.Bind(LoadHeapNumberValue(mutable_heap_number)); 4996 } 4997 Goto(&rebox_double); 4998 } 4999 } 5000 Bind(&if_backing_store); 5001 { 5002 Comment("if_backing_store"); 5003 Node* properties = LoadProperties(object); 5004 field_index = IntPtrSub(field_index, inobject_properties); 5005 Node* value = LoadFixedArrayElement(properties, field_index); 5006 5007 Label if_double(this), if_tagged(this); 5008 Branch(Word32NotEqual(representation, 5009 Int32Constant(Representation::kDouble)), 5010 &if_tagged, &if_double); 5011 Bind(&if_tagged); 5012 { 5013 var_value->Bind(value); 5014 Goto(&done); 5015 } 5016 Bind(&if_double); 5017 { 5018 var_double_value.Bind(LoadHeapNumberValue(value)); 5019 Goto(&rebox_double); 5020 } 5021 } 5022 Bind(&rebox_double); 5023 { 5024 Comment("rebox_double"); 5025 Node* heap_number = AllocateHeapNumberWithValue(var_double_value.value()); 5026 var_value->Bind(heap_number); 5027 Goto(&done); 5028 } 5029 } 5030 Bind(&if_in_descriptor); 5031 { 5032 var_value->Bind( 5033 LoadValueByKeyIndex<DescriptorArray>(descriptors, name_index)); 5034 Goto(&done); 5035 } 5036 Bind(&done); 5037 5038 Comment("] LoadPropertyFromFastObject"); 5039 } 5040 5041 void CodeStubAssembler::LoadPropertyFromNameDictionary(Node* dictionary, 5042 Node* name_index, 5043 Variable* var_details, 5044 Variable* var_value) { 5045 Comment("LoadPropertyFromNameDictionary"); 5046 CSA_ASSERT(this, IsDictionary(dictionary)); 5047 5048 var_details->Bind( 5049 LoadDetailsByKeyIndex<NameDictionary>(dictionary, name_index)); 5050 var_value->Bind(LoadValueByKeyIndex<NameDictionary>(dictionary, name_index)); 5051 5052 Comment("] LoadPropertyFromNameDictionary"); 5053 } 5054 5055 void CodeStubAssembler::LoadPropertyFromGlobalDictionary(Node* dictionary, 5056 Node* name_index, 5057 Variable* var_details, 5058 Variable* var_value, 5059 Label* if_deleted) { 5060 Comment("[ LoadPropertyFromGlobalDictionary"); 5061 CSA_ASSERT(this, IsDictionary(dictionary)); 5062 5063 Node* property_cell = 5064 LoadValueByKeyIndex<GlobalDictionary>(dictionary, name_index); 5065 5066 Node* value = LoadObjectField(property_cell, PropertyCell::kValueOffset); 5067 GotoIf(WordEqual(value, TheHoleConstant()), if_deleted); 5068 5069 var_value->Bind(value); 5070 5071 Node* details = LoadAndUntagToWord32ObjectField(property_cell, 5072 PropertyCell::kDetailsOffset); 5073 var_details->Bind(details); 5074 5075 Comment("] LoadPropertyFromGlobalDictionary"); 5076 } 5077 5078 // |value| is the property backing store's contents, which is either a value 5079 // or an accessor pair, as specified by |details|. 5080 // Returns either the original value, or the result of the getter call. 5081 Node* CodeStubAssembler::CallGetterIfAccessor(Node* value, Node* details, 5082 Node* context, Node* receiver, 5083 Label* if_bailout) { 5084 Variable var_value(this, MachineRepresentation::kTagged, value); 5085 Label done(this); 5086 5087 Node* kind = DecodeWord32<PropertyDetails::KindField>(details); 5088 GotoIf(Word32Equal(kind, Int32Constant(kData)), &done); 5089 5090 // Accessor case. 5091 { 5092 Node* accessor_pair = value; 5093 GotoIf(Word32Equal(LoadInstanceType(accessor_pair), 5094 Int32Constant(ACCESSOR_INFO_TYPE)), 5095 if_bailout); 5096 CSA_ASSERT(this, HasInstanceType(accessor_pair, ACCESSOR_PAIR_TYPE)); 5097 Node* getter = LoadObjectField(accessor_pair, AccessorPair::kGetterOffset); 5098 Node* getter_map = LoadMap(getter); 5099 Node* instance_type = LoadMapInstanceType(getter_map); 5100 // FunctionTemplateInfo getters are not supported yet. 5101 GotoIf( 5102 Word32Equal(instance_type, Int32Constant(FUNCTION_TEMPLATE_INFO_TYPE)), 5103 if_bailout); 5104 5105 // Return undefined if the {getter} is not callable. 5106 var_value.Bind(UndefinedConstant()); 5107 GotoIfNot(IsCallableMap(getter_map), &done); 5108 5109 // Call the accessor. 5110 Callable callable = CodeFactory::Call(isolate()); 5111 Node* result = CallJS(callable, context, getter, receiver); 5112 var_value.Bind(result); 5113 Goto(&done); 5114 } 5115 5116 Bind(&done); 5117 return var_value.value(); 5118 } 5119 5120 void CodeStubAssembler::TryGetOwnProperty( 5121 Node* context, Node* receiver, Node* object, Node* map, Node* instance_type, 5122 Node* unique_name, Label* if_found_value, Variable* var_value, 5123 Label* if_not_found, Label* if_bailout) { 5124 DCHECK_EQ(MachineRepresentation::kTagged, var_value->rep()); 5125 Comment("TryGetOwnProperty"); 5126 5127 Variable var_meta_storage(this, MachineRepresentation::kTagged); 5128 Variable var_entry(this, MachineType::PointerRepresentation()); 5129 5130 Label if_found_fast(this), if_found_dict(this), if_found_global(this); 5131 5132 Variable var_details(this, MachineRepresentation::kWord32); 5133 Variable* vars[] = {var_value, &var_details}; 5134 Label if_found(this, 2, vars); 5135 5136 TryLookupProperty(object, map, instance_type, unique_name, &if_found_fast, 5137 &if_found_dict, &if_found_global, &var_meta_storage, 5138 &var_entry, if_not_found, if_bailout); 5139 Bind(&if_found_fast); 5140 { 5141 Node* descriptors = var_meta_storage.value(); 5142 Node* name_index = var_entry.value(); 5143 5144 LoadPropertyFromFastObject(object, map, descriptors, name_index, 5145 &var_details, var_value); 5146 Goto(&if_found); 5147 } 5148 Bind(&if_found_dict); 5149 { 5150 Node* dictionary = var_meta_storage.value(); 5151 Node* entry = var_entry.value(); 5152 LoadPropertyFromNameDictionary(dictionary, entry, &var_details, var_value); 5153 Goto(&if_found); 5154 } 5155 Bind(&if_found_global); 5156 { 5157 Node* dictionary = var_meta_storage.value(); 5158 Node* entry = var_entry.value(); 5159 5160 LoadPropertyFromGlobalDictionary(dictionary, entry, &var_details, var_value, 5161 if_not_found); 5162 Goto(&if_found); 5163 } 5164 // Here we have details and value which could be an accessor. 5165 Bind(&if_found); 5166 { 5167 Node* value = CallGetterIfAccessor(var_value->value(), var_details.value(), 5168 context, receiver, if_bailout); 5169 var_value->Bind(value); 5170 Goto(if_found_value); 5171 } 5172 } 5173 5174 void CodeStubAssembler::TryLookupElement(Node* object, Node* map, 5175 Node* instance_type, 5176 Node* intptr_index, Label* if_found, 5177 Label* if_not_found, 5178 Label* if_bailout) { 5179 // Handle special objects in runtime. 5180 GotoIf(Int32LessThanOrEqual(instance_type, 5181 Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)), 5182 if_bailout); 5183 5184 Node* elements_kind = LoadMapElementsKind(map); 5185 5186 // TODO(verwaest): Support other elements kinds as well. 5187 Label if_isobjectorsmi(this), if_isdouble(this), if_isdictionary(this), 5188 if_isfaststringwrapper(this), if_isslowstringwrapper(this), if_oob(this); 5189 // clang-format off 5190 int32_t values[] = { 5191 // Handled by {if_isobjectorsmi}. 5192 FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS, 5193 FAST_HOLEY_ELEMENTS, 5194 // Handled by {if_isdouble}. 5195 FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS, 5196 // Handled by {if_isdictionary}. 5197 DICTIONARY_ELEMENTS, 5198 // Handled by {if_isfaststringwrapper}. 5199 FAST_STRING_WRAPPER_ELEMENTS, 5200 // Handled by {if_isslowstringwrapper}. 5201 SLOW_STRING_WRAPPER_ELEMENTS, 5202 // Handled by {if_not_found}. 5203 NO_ELEMENTS, 5204 }; 5205 Label* labels[] = { 5206 &if_isobjectorsmi, &if_isobjectorsmi, &if_isobjectorsmi, 5207 &if_isobjectorsmi, 5208 &if_isdouble, &if_isdouble, 5209 &if_isdictionary, 5210 &if_isfaststringwrapper, 5211 &if_isslowstringwrapper, 5212 if_not_found, 5213 }; 5214 // clang-format on 5215 STATIC_ASSERT(arraysize(values) == arraysize(labels)); 5216 Switch(elements_kind, if_bailout, values, labels, arraysize(values)); 5217 5218 Bind(&if_isobjectorsmi); 5219 { 5220 Node* elements = LoadElements(object); 5221 Node* length = LoadAndUntagFixedArrayBaseLength(elements); 5222 5223 GotoIfNot(UintPtrLessThan(intptr_index, length), &if_oob); 5224 5225 Node* element = LoadFixedArrayElement(elements, intptr_index); 5226 Node* the_hole = TheHoleConstant(); 5227 Branch(WordEqual(element, the_hole), if_not_found, if_found); 5228 } 5229 Bind(&if_isdouble); 5230 { 5231 Node* elements = LoadElements(object); 5232 Node* length = LoadAndUntagFixedArrayBaseLength(elements); 5233 5234 GotoIfNot(UintPtrLessThan(intptr_index, length), &if_oob); 5235 5236 // Check if the element is a double hole, but don't load it. 5237 LoadFixedDoubleArrayElement(elements, intptr_index, MachineType::None(), 0, 5238 INTPTR_PARAMETERS, if_not_found); 5239 Goto(if_found); 5240 } 5241 Bind(&if_isdictionary); 5242 { 5243 // Negative keys must be converted to property names. 5244 GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), if_bailout); 5245 5246 Variable var_entry(this, MachineType::PointerRepresentation()); 5247 Node* elements = LoadElements(object); 5248 NumberDictionaryLookup<SeededNumberDictionary>( 5249 elements, intptr_index, if_found, &var_entry, if_not_found); 5250 } 5251 Bind(&if_isfaststringwrapper); 5252 { 5253 CSA_ASSERT(this, HasInstanceType(object, JS_VALUE_TYPE)); 5254 Node* string = LoadJSValueValue(object); 5255 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); 5256 Node* length = LoadStringLength(string); 5257 GotoIf(UintPtrLessThan(intptr_index, SmiUntag(length)), if_found); 5258 Goto(&if_isobjectorsmi); 5259 } 5260 Bind(&if_isslowstringwrapper); 5261 { 5262 CSA_ASSERT(this, HasInstanceType(object, JS_VALUE_TYPE)); 5263 Node* string = LoadJSValueValue(object); 5264 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); 5265 Node* length = LoadStringLength(string); 5266 GotoIf(UintPtrLessThan(intptr_index, SmiUntag(length)), if_found); 5267 Goto(&if_isdictionary); 5268 } 5269 Bind(&if_oob); 5270 { 5271 // Positive OOB indices mean "not found", negative indices must be 5272 // converted to property names. 5273 GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), if_bailout); 5274 Goto(if_not_found); 5275 } 5276 } 5277 5278 // Instantiate template methods to workaround GCC compilation issue. 5279 template void CodeStubAssembler::NumberDictionaryLookup<SeededNumberDictionary>( 5280 Node*, Node*, Label*, Variable*, Label*); 5281 template void CodeStubAssembler::NumberDictionaryLookup< 5282 UnseededNumberDictionary>(Node*, Node*, Label*, Variable*, Label*); 5283 5284 void CodeStubAssembler::TryPrototypeChainLookup( 5285 Node* receiver, Node* key, const LookupInHolder& lookup_property_in_holder, 5286 const LookupInHolder& lookup_element_in_holder, Label* if_end, 5287 Label* if_bailout) { 5288 // Ensure receiver is JSReceiver, otherwise bailout. 5289 Label if_objectisnotsmi(this); 5290 Branch(TaggedIsSmi(receiver), if_bailout, &if_objectisnotsmi); 5291 Bind(&if_objectisnotsmi); 5292 5293 Node* map = LoadMap(receiver); 5294 Node* instance_type = LoadMapInstanceType(map); 5295 { 5296 Label if_objectisreceiver(this); 5297 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 5298 STATIC_ASSERT(FIRST_JS_RECEIVER_TYPE == JS_PROXY_TYPE); 5299 Branch( 5300 Int32GreaterThan(instance_type, Int32Constant(FIRST_JS_RECEIVER_TYPE)), 5301 &if_objectisreceiver, if_bailout); 5302 Bind(&if_objectisreceiver); 5303 } 5304 5305 Variable var_index(this, MachineType::PointerRepresentation()); 5306 Variable var_unique(this, MachineRepresentation::kTagged); 5307 5308 Label if_keyisindex(this), if_iskeyunique(this); 5309 TryToName(key, &if_keyisindex, &var_index, &if_iskeyunique, &var_unique, 5310 if_bailout); 5311 5312 Bind(&if_iskeyunique); 5313 { 5314 Variable var_holder(this, MachineRepresentation::kTagged, receiver); 5315 Variable var_holder_map(this, MachineRepresentation::kTagged, map); 5316 Variable var_holder_instance_type(this, MachineRepresentation::kWord32, 5317 instance_type); 5318 5319 Variable* merged_variables[] = {&var_holder, &var_holder_map, 5320 &var_holder_instance_type}; 5321 Label loop(this, arraysize(merged_variables), merged_variables); 5322 Goto(&loop); 5323 Bind(&loop); 5324 { 5325 Node* holder_map = var_holder_map.value(); 5326 Node* holder_instance_type = var_holder_instance_type.value(); 5327 5328 Label next_proto(this); 5329 lookup_property_in_holder(receiver, var_holder.value(), holder_map, 5330 holder_instance_type, var_unique.value(), 5331 &next_proto, if_bailout); 5332 Bind(&next_proto); 5333 5334 // Bailout if it can be an integer indexed exotic case. 5335 GotoIf( 5336 Word32Equal(holder_instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)), 5337 if_bailout); 5338 5339 Node* proto = LoadMapPrototype(holder_map); 5340 5341 Label if_not_null(this); 5342 Branch(WordEqual(proto, NullConstant()), if_end, &if_not_null); 5343 Bind(&if_not_null); 5344 5345 Node* map = LoadMap(proto); 5346 Node* instance_type = LoadMapInstanceType(map); 5347 5348 var_holder.Bind(proto); 5349 var_holder_map.Bind(map); 5350 var_holder_instance_type.Bind(instance_type); 5351 Goto(&loop); 5352 } 5353 } 5354 Bind(&if_keyisindex); 5355 { 5356 Variable var_holder(this, MachineRepresentation::kTagged, receiver); 5357 Variable var_holder_map(this, MachineRepresentation::kTagged, map); 5358 Variable var_holder_instance_type(this, MachineRepresentation::kWord32, 5359 instance_type); 5360 5361 Variable* merged_variables[] = {&var_holder, &var_holder_map, 5362 &var_holder_instance_type}; 5363 Label loop(this, arraysize(merged_variables), merged_variables); 5364 Goto(&loop); 5365 Bind(&loop); 5366 { 5367 Label next_proto(this); 5368 lookup_element_in_holder(receiver, var_holder.value(), 5369 var_holder_map.value(), 5370 var_holder_instance_type.value(), 5371 var_index.value(), &next_proto, if_bailout); 5372 Bind(&next_proto); 5373 5374 Node* proto = LoadMapPrototype(var_holder_map.value()); 5375 5376 Label if_not_null(this); 5377 Branch(WordEqual(proto, NullConstant()), if_end, &if_not_null); 5378 Bind(&if_not_null); 5379 5380 Node* map = LoadMap(proto); 5381 Node* instance_type = LoadMapInstanceType(map); 5382 5383 var_holder.Bind(proto); 5384 var_holder_map.Bind(map); 5385 var_holder_instance_type.Bind(instance_type); 5386 Goto(&loop); 5387 } 5388 } 5389 } 5390 5391 Node* CodeStubAssembler::OrdinaryHasInstance(Node* context, Node* callable, 5392 Node* object) { 5393 Variable var_result(this, MachineRepresentation::kTagged); 5394 Label return_false(this), return_true(this), 5395 return_runtime(this, Label::kDeferred), return_result(this); 5396 5397 // Goto runtime if {object} is a Smi. 5398 GotoIf(TaggedIsSmi(object), &return_runtime); 5399 5400 // Load map of {object}. 5401 Node* object_map = LoadMap(object); 5402 5403 // Lookup the {callable} and {object} map in the global instanceof cache. 5404 // Note: This is safe because we clear the global instanceof cache whenever 5405 // we change the prototype of any object. 5406 Node* instanceof_cache_function = 5407 LoadRoot(Heap::kInstanceofCacheFunctionRootIndex); 5408 Node* instanceof_cache_map = LoadRoot(Heap::kInstanceofCacheMapRootIndex); 5409 { 5410 Label instanceof_cache_miss(this); 5411 GotoIfNot(WordEqual(instanceof_cache_function, callable), 5412 &instanceof_cache_miss); 5413 GotoIfNot(WordEqual(instanceof_cache_map, object_map), 5414 &instanceof_cache_miss); 5415 var_result.Bind(LoadRoot(Heap::kInstanceofCacheAnswerRootIndex)); 5416 Goto(&return_result); 5417 Bind(&instanceof_cache_miss); 5418 } 5419 5420 // Goto runtime if {callable} is a Smi. 5421 GotoIf(TaggedIsSmi(callable), &return_runtime); 5422 5423 // Load map of {callable}. 5424 Node* callable_map = LoadMap(callable); 5425 5426 // Goto runtime if {callable} is not a JSFunction. 5427 Node* callable_instance_type = LoadMapInstanceType(callable_map); 5428 GotoIfNot( 5429 Word32Equal(callable_instance_type, Int32Constant(JS_FUNCTION_TYPE)), 5430 &return_runtime); 5431 5432 // Goto runtime if {callable} is not a constructor or has 5433 // a non-instance "prototype". 5434 Node* callable_bitfield = LoadMapBitField(callable_map); 5435 GotoIfNot( 5436 Word32Equal(Word32And(callable_bitfield, 5437 Int32Constant((1 << Map::kHasNonInstancePrototype) | 5438 (1 << Map::kIsConstructor))), 5439 Int32Constant(1 << Map::kIsConstructor)), 5440 &return_runtime); 5441 5442 // Get the "prototype" (or initial map) of the {callable}. 5443 Node* callable_prototype = 5444 LoadObjectField(callable, JSFunction::kPrototypeOrInitialMapOffset); 5445 { 5446 Label callable_prototype_valid(this); 5447 Variable var_callable_prototype(this, MachineRepresentation::kTagged, 5448 callable_prototype); 5449 5450 // Resolve the "prototype" if the {callable} has an initial map. Afterwards 5451 // the {callable_prototype} will be either the JSReceiver prototype object 5452 // or the hole value, which means that no instances of the {callable} were 5453 // created so far and hence we should return false. 5454 Node* callable_prototype_instance_type = 5455 LoadInstanceType(callable_prototype); 5456 GotoIfNot( 5457 Word32Equal(callable_prototype_instance_type, Int32Constant(MAP_TYPE)), 5458 &callable_prototype_valid); 5459 var_callable_prototype.Bind( 5460 LoadObjectField(callable_prototype, Map::kPrototypeOffset)); 5461 Goto(&callable_prototype_valid); 5462 Bind(&callable_prototype_valid); 5463 callable_prototype = var_callable_prototype.value(); 5464 } 5465 5466 // Update the global instanceof cache with the current {object} map and 5467 // {callable}. The cached answer will be set when it is known below. 5468 StoreRoot(Heap::kInstanceofCacheFunctionRootIndex, callable); 5469 StoreRoot(Heap::kInstanceofCacheMapRootIndex, object_map); 5470 5471 // Loop through the prototype chain looking for the {callable} prototype. 5472 Variable var_object_map(this, MachineRepresentation::kTagged, object_map); 5473 Label loop(this, &var_object_map); 5474 Goto(&loop); 5475 Bind(&loop); 5476 { 5477 Node* object_map = var_object_map.value(); 5478 5479 // Check if the current {object} needs to be access checked. 5480 Node* object_bitfield = LoadMapBitField(object_map); 5481 GotoIfNot( 5482 Word32Equal(Word32And(object_bitfield, 5483 Int32Constant(1 << Map::kIsAccessCheckNeeded)), 5484 Int32Constant(0)), 5485 &return_runtime); 5486 5487 // Check if the current {object} is a proxy. 5488 Node* object_instance_type = LoadMapInstanceType(object_map); 5489 GotoIf(Word32Equal(object_instance_type, Int32Constant(JS_PROXY_TYPE)), 5490 &return_runtime); 5491 5492 // Check the current {object} prototype. 5493 Node* object_prototype = LoadMapPrototype(object_map); 5494 GotoIf(WordEqual(object_prototype, NullConstant()), &return_false); 5495 GotoIf(WordEqual(object_prototype, callable_prototype), &return_true); 5496 5497 // Continue with the prototype. 5498 var_object_map.Bind(LoadMap(object_prototype)); 5499 Goto(&loop); 5500 } 5501 5502 Bind(&return_true); 5503 StoreRoot(Heap::kInstanceofCacheAnswerRootIndex, BooleanConstant(true)); 5504 var_result.Bind(BooleanConstant(true)); 5505 Goto(&return_result); 5506 5507 Bind(&return_false); 5508 StoreRoot(Heap::kInstanceofCacheAnswerRootIndex, BooleanConstant(false)); 5509 var_result.Bind(BooleanConstant(false)); 5510 Goto(&return_result); 5511 5512 Bind(&return_runtime); 5513 { 5514 // Invalidate the global instanceof cache. 5515 StoreRoot(Heap::kInstanceofCacheFunctionRootIndex, SmiConstant(0)); 5516 // Fallback to the runtime implementation. 5517 var_result.Bind( 5518 CallRuntime(Runtime::kOrdinaryHasInstance, context, callable, object)); 5519 } 5520 Goto(&return_result); 5521 5522 Bind(&return_result); 5523 return var_result.value(); 5524 } 5525 5526 Node* CodeStubAssembler::ElementOffsetFromIndex(Node* index_node, 5527 ElementsKind kind, 5528 ParameterMode mode, 5529 int base_size) { 5530 int element_size_shift = ElementsKindToShiftSize(kind); 5531 int element_size = 1 << element_size_shift; 5532 int const kSmiShiftBits = kSmiShiftSize + kSmiTagSize; 5533 intptr_t index = 0; 5534 bool constant_index = false; 5535 if (mode == SMI_PARAMETERS) { 5536 element_size_shift -= kSmiShiftBits; 5537 Smi* smi_index; 5538 constant_index = ToSmiConstant(index_node, smi_index); 5539 if (constant_index) index = smi_index->value(); 5540 index_node = BitcastTaggedToWord(index_node); 5541 } else { 5542 DCHECK(mode == INTPTR_PARAMETERS); 5543 constant_index = ToIntPtrConstant(index_node, index); 5544 } 5545 if (constant_index) { 5546 return IntPtrConstant(base_size + element_size * index); 5547 } 5548 5549 Node* shifted_index = 5550 (element_size_shift == 0) 5551 ? index_node 5552 : ((element_size_shift > 0) 5553 ? WordShl(index_node, IntPtrConstant(element_size_shift)) 5554 : WordShr(index_node, IntPtrConstant(-element_size_shift))); 5555 return IntPtrAdd(IntPtrConstant(base_size), shifted_index); 5556 } 5557 5558 Node* CodeStubAssembler::LoadFeedbackVectorForStub() { 5559 Node* function = 5560 LoadFromParentFrame(JavaScriptFrameConstants::kFunctionOffset); 5561 Node* cell = LoadObjectField(function, JSFunction::kFeedbackVectorOffset); 5562 return LoadObjectField(cell, Cell::kValueOffset); 5563 } 5564 5565 void CodeStubAssembler::UpdateFeedback(Node* feedback, Node* feedback_vector, 5566 Node* slot_id) { 5567 // This method is used for binary op and compare feedback. These 5568 // vector nodes are initialized with a smi 0, so we can simply OR 5569 // our new feedback in place. 5570 Node* previous_feedback = LoadFixedArrayElement(feedback_vector, slot_id); 5571 Node* combined_feedback = SmiOr(previous_feedback, feedback); 5572 StoreFixedArrayElement(feedback_vector, slot_id, combined_feedback, 5573 SKIP_WRITE_BARRIER); 5574 } 5575 5576 Node* CodeStubAssembler::LoadReceiverMap(Node* receiver) { 5577 Variable var_receiver_map(this, MachineRepresentation::kTagged); 5578 Label load_smi_map(this, Label::kDeferred), load_receiver_map(this), 5579 if_result(this); 5580 5581 Branch(TaggedIsSmi(receiver), &load_smi_map, &load_receiver_map); 5582 Bind(&load_smi_map); 5583 { 5584 var_receiver_map.Bind(LoadRoot(Heap::kHeapNumberMapRootIndex)); 5585 Goto(&if_result); 5586 } 5587 Bind(&load_receiver_map); 5588 { 5589 var_receiver_map.Bind(LoadMap(receiver)); 5590 Goto(&if_result); 5591 } 5592 Bind(&if_result); 5593 return var_receiver_map.value(); 5594 } 5595 5596 Node* CodeStubAssembler::TryToIntptr(Node* key, Label* miss) { 5597 Variable var_intptr_key(this, MachineType::PointerRepresentation()); 5598 Label done(this, &var_intptr_key), key_is_smi(this); 5599 GotoIf(TaggedIsSmi(key), &key_is_smi); 5600 // Try to convert a heap number to a Smi. 5601 GotoIfNot(IsHeapNumberMap(LoadMap(key)), miss); 5602 { 5603 Node* value = LoadHeapNumberValue(key); 5604 Node* int_value = RoundFloat64ToInt32(value); 5605 GotoIfNot(Float64Equal(value, ChangeInt32ToFloat64(int_value)), miss); 5606 var_intptr_key.Bind(ChangeInt32ToIntPtr(int_value)); 5607 Goto(&done); 5608 } 5609 5610 Bind(&key_is_smi); 5611 { 5612 var_intptr_key.Bind(SmiUntag(key)); 5613 Goto(&done); 5614 } 5615 5616 Bind(&done); 5617 return var_intptr_key.value(); 5618 } 5619 5620 Node* CodeStubAssembler::EmitKeyedSloppyArguments(Node* receiver, Node* key, 5621 Node* value, Label* bailout) { 5622 // Mapped arguments are actual arguments. Unmapped arguments are values added 5623 // to the arguments object after it was created for the call. Mapped arguments 5624 // are stored in the context at indexes given by elements[key + 2]. Unmapped 5625 // arguments are stored as regular indexed properties in the arguments array, 5626 // held at elements[1]. See NewSloppyArguments() in runtime.cc for a detailed 5627 // look at argument object construction. 5628 // 5629 // The sloppy arguments elements array has a special format: 5630 // 5631 // 0: context 5632 // 1: unmapped arguments array 5633 // 2: mapped_index0, 5634 // 3: mapped_index1, 5635 // ... 5636 // 5637 // length is 2 + min(number_of_actual_arguments, number_of_formal_arguments). 5638 // If key + 2 >= elements.length then attempt to look in the unmapped 5639 // arguments array (given by elements[1]) and return the value at key, missing 5640 // to the runtime if the unmapped arguments array is not a fixed array or if 5641 // key >= unmapped_arguments_array.length. 5642 // 5643 // Otherwise, t = elements[key + 2]. If t is the hole, then look up the value 5644 // in the unmapped arguments array, as described above. Otherwise, t is a Smi 5645 // index into the context array given at elements[0]. Return the value at 5646 // context[t]. 5647 5648 bool is_load = value == nullptr; 5649 5650 GotoIfNot(TaggedIsSmi(key), bailout); 5651 key = SmiUntag(key); 5652 GotoIf(IntPtrLessThan(key, IntPtrConstant(0)), bailout); 5653 5654 Node* elements = LoadElements(receiver); 5655 Node* elements_length = LoadAndUntagFixedArrayBaseLength(elements); 5656 5657 Variable var_result(this, MachineRepresentation::kTagged); 5658 if (!is_load) { 5659 var_result.Bind(value); 5660 } 5661 Label if_mapped(this), if_unmapped(this), end(this, &var_result); 5662 Node* intptr_two = IntPtrConstant(2); 5663 Node* adjusted_length = IntPtrSub(elements_length, intptr_two); 5664 5665 GotoIf(UintPtrGreaterThanOrEqual(key, adjusted_length), &if_unmapped); 5666 5667 Node* mapped_index = 5668 LoadFixedArrayElement(elements, IntPtrAdd(key, intptr_two)); 5669 Branch(WordEqual(mapped_index, TheHoleConstant()), &if_unmapped, &if_mapped); 5670 5671 Bind(&if_mapped); 5672 { 5673 CSA_ASSERT(this, TaggedIsSmi(mapped_index)); 5674 mapped_index = SmiUntag(mapped_index); 5675 Node* the_context = LoadFixedArrayElement(elements, 0); 5676 // Assert that we can use LoadFixedArrayElement/StoreFixedArrayElement 5677 // methods for accessing Context. 5678 STATIC_ASSERT(Context::kHeaderSize == FixedArray::kHeaderSize); 5679 DCHECK_EQ(Context::SlotOffset(0) + kHeapObjectTag, 5680 FixedArray::OffsetOfElementAt(0)); 5681 if (is_load) { 5682 Node* result = LoadFixedArrayElement(the_context, mapped_index); 5683 CSA_ASSERT(this, WordNotEqual(result, TheHoleConstant())); 5684 var_result.Bind(result); 5685 } else { 5686 StoreFixedArrayElement(the_context, mapped_index, value); 5687 } 5688 Goto(&end); 5689 } 5690 5691 Bind(&if_unmapped); 5692 { 5693 Node* backing_store = LoadFixedArrayElement(elements, 1); 5694 GotoIf(WordNotEqual(LoadMap(backing_store), FixedArrayMapConstant()), 5695 bailout); 5696 5697 Node* backing_store_length = 5698 LoadAndUntagFixedArrayBaseLength(backing_store); 5699 GotoIf(UintPtrGreaterThanOrEqual(key, backing_store_length), bailout); 5700 5701 // The key falls into unmapped range. 5702 if (is_load) { 5703 Node* result = LoadFixedArrayElement(backing_store, key); 5704 GotoIf(WordEqual(result, TheHoleConstant()), bailout); 5705 var_result.Bind(result); 5706 } else { 5707 StoreFixedArrayElement(backing_store, key, value); 5708 } 5709 Goto(&end); 5710 } 5711 5712 Bind(&end); 5713 return var_result.value(); 5714 } 5715 5716 Node* CodeStubAssembler::LoadScriptContext(Node* context, int context_index) { 5717 Node* native_context = LoadNativeContext(context); 5718 Node* script_context_table = 5719 LoadContextElement(native_context, Context::SCRIPT_CONTEXT_TABLE_INDEX); 5720 5721 int offset = 5722 ScriptContextTable::GetContextOffset(context_index) - kHeapObjectTag; 5723 return Load(MachineType::AnyTagged(), script_context_table, 5724 IntPtrConstant(offset)); 5725 } 5726 5727 namespace { 5728 5729 // Converts typed array elements kind to a machine representations. 5730 MachineRepresentation ElementsKindToMachineRepresentation(ElementsKind kind) { 5731 switch (kind) { 5732 case UINT8_CLAMPED_ELEMENTS: 5733 case UINT8_ELEMENTS: 5734 case INT8_ELEMENTS: 5735 return MachineRepresentation::kWord8; 5736 case UINT16_ELEMENTS: 5737 case INT16_ELEMENTS: 5738 return MachineRepresentation::kWord16; 5739 case UINT32_ELEMENTS: 5740 case INT32_ELEMENTS: 5741 return MachineRepresentation::kWord32; 5742 case FLOAT32_ELEMENTS: 5743 return MachineRepresentation::kFloat32; 5744 case FLOAT64_ELEMENTS: 5745 return MachineRepresentation::kFloat64; 5746 default: 5747 UNREACHABLE(); 5748 return MachineRepresentation::kNone; 5749 } 5750 } 5751 5752 } // namespace 5753 5754 void CodeStubAssembler::StoreElement(Node* elements, ElementsKind kind, 5755 Node* index, Node* value, 5756 ParameterMode mode) { 5757 if (IsFixedTypedArrayElementsKind(kind)) { 5758 if (kind == UINT8_CLAMPED_ELEMENTS) { 5759 CSA_ASSERT(this, 5760 Word32Equal(value, Word32And(Int32Constant(0xff), value))); 5761 } 5762 Node* offset = ElementOffsetFromIndex(index, kind, mode, 0); 5763 MachineRepresentation rep = ElementsKindToMachineRepresentation(kind); 5764 StoreNoWriteBarrier(rep, elements, offset, value); 5765 return; 5766 } 5767 5768 WriteBarrierMode barrier_mode = 5769 IsFastSmiElementsKind(kind) ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER; 5770 if (IsFastDoubleElementsKind(kind)) { 5771 // Make sure we do not store signalling NaNs into double arrays. 5772 value = Float64SilenceNaN(value); 5773 StoreFixedDoubleArrayElement(elements, index, value, mode); 5774 } else { 5775 StoreFixedArrayElement(elements, index, value, barrier_mode, 0, mode); 5776 } 5777 } 5778 5779 Node* CodeStubAssembler::Int32ToUint8Clamped(Node* int32_value) { 5780 Label done(this); 5781 Node* int32_zero = Int32Constant(0); 5782 Node* int32_255 = Int32Constant(255); 5783 Variable var_value(this, MachineRepresentation::kWord32, int32_value); 5784 GotoIf(Uint32LessThanOrEqual(int32_value, int32_255), &done); 5785 var_value.Bind(int32_zero); 5786 GotoIf(Int32LessThan(int32_value, int32_zero), &done); 5787 var_value.Bind(int32_255); 5788 Goto(&done); 5789 Bind(&done); 5790 return var_value.value(); 5791 } 5792 5793 Node* CodeStubAssembler::Float64ToUint8Clamped(Node* float64_value) { 5794 Label done(this); 5795 Variable var_value(this, MachineRepresentation::kWord32, Int32Constant(0)); 5796 GotoIf(Float64LessThanOrEqual(float64_value, Float64Constant(0.0)), &done); 5797 var_value.Bind(Int32Constant(255)); 5798 GotoIf(Float64LessThanOrEqual(Float64Constant(255.0), float64_value), &done); 5799 { 5800 Node* rounded_value = Float64RoundToEven(float64_value); 5801 var_value.Bind(TruncateFloat64ToWord32(rounded_value)); 5802 Goto(&done); 5803 } 5804 Bind(&done); 5805 return var_value.value(); 5806 } 5807 5808 Node* CodeStubAssembler::PrepareValueForWriteToTypedArray( 5809 Node* input, ElementsKind elements_kind, Label* bailout) { 5810 DCHECK(IsFixedTypedArrayElementsKind(elements_kind)); 5811 5812 MachineRepresentation rep; 5813 switch (elements_kind) { 5814 case UINT8_ELEMENTS: 5815 case INT8_ELEMENTS: 5816 case UINT16_ELEMENTS: 5817 case INT16_ELEMENTS: 5818 case UINT32_ELEMENTS: 5819 case INT32_ELEMENTS: 5820 case UINT8_CLAMPED_ELEMENTS: 5821 rep = MachineRepresentation::kWord32; 5822 break; 5823 case FLOAT32_ELEMENTS: 5824 rep = MachineRepresentation::kFloat32; 5825 break; 5826 case FLOAT64_ELEMENTS: 5827 rep = MachineRepresentation::kFloat64; 5828 break; 5829 default: 5830 UNREACHABLE(); 5831 return nullptr; 5832 } 5833 5834 Variable var_result(this, rep); 5835 Label done(this, &var_result), if_smi(this); 5836 GotoIf(TaggedIsSmi(input), &if_smi); 5837 // Try to convert a heap number to a Smi. 5838 GotoIfNot(IsHeapNumberMap(LoadMap(input)), bailout); 5839 { 5840 Node* value = LoadHeapNumberValue(input); 5841 if (rep == MachineRepresentation::kWord32) { 5842 if (elements_kind == UINT8_CLAMPED_ELEMENTS) { 5843 value = Float64ToUint8Clamped(value); 5844 } else { 5845 value = TruncateFloat64ToWord32(value); 5846 } 5847 } else if (rep == MachineRepresentation::kFloat32) { 5848 value = TruncateFloat64ToFloat32(value); 5849 } else { 5850 DCHECK_EQ(MachineRepresentation::kFloat64, rep); 5851 } 5852 var_result.Bind(value); 5853 Goto(&done); 5854 } 5855 5856 Bind(&if_smi); 5857 { 5858 Node* value = SmiToWord32(input); 5859 if (rep == MachineRepresentation::kFloat32) { 5860 value = RoundInt32ToFloat32(value); 5861 } else if (rep == MachineRepresentation::kFloat64) { 5862 value = ChangeInt32ToFloat64(value); 5863 } else { 5864 DCHECK_EQ(MachineRepresentation::kWord32, rep); 5865 if (elements_kind == UINT8_CLAMPED_ELEMENTS) { 5866 value = Int32ToUint8Clamped(value); 5867 } 5868 } 5869 var_result.Bind(value); 5870 Goto(&done); 5871 } 5872 5873 Bind(&done); 5874 return var_result.value(); 5875 } 5876 5877 void CodeStubAssembler::EmitElementStore(Node* object, Node* key, Node* value, 5878 bool is_jsarray, 5879 ElementsKind elements_kind, 5880 KeyedAccessStoreMode store_mode, 5881 Label* bailout) { 5882 Node* elements = LoadElements(object); 5883 if (IsFastSmiOrObjectElementsKind(elements_kind) && 5884 store_mode != STORE_NO_TRANSITION_HANDLE_COW) { 5885 // Bailout in case of COW elements. 5886 GotoIf(WordNotEqual(LoadMap(elements), 5887 LoadRoot(Heap::kFixedArrayMapRootIndex)), 5888 bailout); 5889 } 5890 // TODO(ishell): introduce TryToIntPtrOrSmi() and use OptimalParameterMode(). 5891 ParameterMode parameter_mode = INTPTR_PARAMETERS; 5892 key = TryToIntptr(key, bailout); 5893 5894 if (IsFixedTypedArrayElementsKind(elements_kind)) { 5895 Label done(this); 5896 // TODO(ishell): call ToNumber() on value and don't bailout but be careful 5897 // to call it only once if we decide to bailout because of bounds checks. 5898 5899 value = PrepareValueForWriteToTypedArray(value, elements_kind, bailout); 5900 5901 // There must be no allocations between the buffer load and 5902 // and the actual store to backing store, because GC may decide that 5903 // the buffer is not alive or move the elements. 5904 // TODO(ishell): introduce DisallowHeapAllocationCode scope here. 5905 5906 // Check if buffer has been neutered. 5907 Node* buffer = LoadObjectField(object, JSArrayBufferView::kBufferOffset); 5908 GotoIf(IsDetachedBuffer(buffer), bailout); 5909 5910 // Bounds check. 5911 Node* length = TaggedToParameter( 5912 LoadObjectField(object, JSTypedArray::kLengthOffset), parameter_mode); 5913 5914 if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) { 5915 // Skip the store if we write beyond the length. 5916 GotoIfNot(IntPtrLessThan(key, length), &done); 5917 // ... but bailout if the key is negative. 5918 } else { 5919 DCHECK_EQ(STANDARD_STORE, store_mode); 5920 } 5921 GotoIfNot(UintPtrLessThan(key, length), bailout); 5922 5923 // Backing store = external_pointer + base_pointer. 5924 Node* external_pointer = 5925 LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset, 5926 MachineType::Pointer()); 5927 Node* base_pointer = 5928 LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset); 5929 Node* backing_store = 5930 IntPtrAdd(external_pointer, BitcastTaggedToWord(base_pointer)); 5931 StoreElement(backing_store, elements_kind, key, value, parameter_mode); 5932 Goto(&done); 5933 5934 Bind(&done); 5935 return; 5936 } 5937 DCHECK(IsFastSmiOrObjectElementsKind(elements_kind) || 5938 IsFastDoubleElementsKind(elements_kind)); 5939 5940 Node* length = is_jsarray ? LoadObjectField(object, JSArray::kLengthOffset) 5941 : LoadFixedArrayBaseLength(elements); 5942 length = TaggedToParameter(length, parameter_mode); 5943 5944 // In case value is stored into a fast smi array, assure that the value is 5945 // a smi before manipulating the backing store. Otherwise the backing store 5946 // may be left in an invalid state. 5947 if (IsFastSmiElementsKind(elements_kind)) { 5948 GotoIfNot(TaggedIsSmi(value), bailout); 5949 } else if (IsFastDoubleElementsKind(elements_kind)) { 5950 value = TryTaggedToFloat64(value, bailout); 5951 } 5952 5953 if (IsGrowStoreMode(store_mode)) { 5954 elements = CheckForCapacityGrow(object, elements, elements_kind, length, 5955 key, parameter_mode, is_jsarray, bailout); 5956 } else { 5957 GotoIfNot(UintPtrLessThan(key, length), bailout); 5958 5959 if ((store_mode == STORE_NO_TRANSITION_HANDLE_COW) && 5960 IsFastSmiOrObjectElementsKind(elements_kind)) { 5961 elements = CopyElementsOnWrite(object, elements, elements_kind, length, 5962 parameter_mode, bailout); 5963 } 5964 } 5965 StoreElement(elements, elements_kind, key, value, parameter_mode); 5966 } 5967 5968 Node* CodeStubAssembler::CheckForCapacityGrow(Node* object, Node* elements, 5969 ElementsKind kind, Node* length, 5970 Node* key, ParameterMode mode, 5971 bool is_js_array, 5972 Label* bailout) { 5973 Variable checked_elements(this, MachineRepresentation::kTagged); 5974 Label grow_case(this), no_grow_case(this), done(this); 5975 5976 Node* condition; 5977 if (IsHoleyElementsKind(kind)) { 5978 condition = UintPtrGreaterThanOrEqual(key, length); 5979 } else { 5980 condition = WordEqual(key, length); 5981 } 5982 Branch(condition, &grow_case, &no_grow_case); 5983 5984 Bind(&grow_case); 5985 { 5986 Node* current_capacity = 5987 TaggedToParameter(LoadFixedArrayBaseLength(elements), mode); 5988 5989 checked_elements.Bind(elements); 5990 5991 Label fits_capacity(this); 5992 GotoIf(UintPtrLessThan(key, current_capacity), &fits_capacity); 5993 { 5994 Node* new_elements = TryGrowElementsCapacity( 5995 object, elements, kind, key, current_capacity, mode, bailout); 5996 5997 checked_elements.Bind(new_elements); 5998 Goto(&fits_capacity); 5999 } 6000 Bind(&fits_capacity); 6001 6002 if (is_js_array) { 6003 Node* new_length = IntPtrAdd(key, IntPtrOrSmiConstant(1, mode)); 6004 StoreObjectFieldNoWriteBarrier(object, JSArray::kLengthOffset, 6005 ParameterToTagged(new_length, mode)); 6006 } 6007 Goto(&done); 6008 } 6009 6010 Bind(&no_grow_case); 6011 { 6012 GotoIfNot(UintPtrLessThan(key, length), bailout); 6013 checked_elements.Bind(elements); 6014 Goto(&done); 6015 } 6016 6017 Bind(&done); 6018 return checked_elements.value(); 6019 } 6020 6021 Node* CodeStubAssembler::CopyElementsOnWrite(Node* object, Node* elements, 6022 ElementsKind kind, Node* length, 6023 ParameterMode mode, 6024 Label* bailout) { 6025 Variable new_elements_var(this, MachineRepresentation::kTagged, elements); 6026 Label done(this); 6027 6028 GotoIfNot( 6029 WordEqual(LoadMap(elements), LoadRoot(Heap::kFixedCOWArrayMapRootIndex)), 6030 &done); 6031 { 6032 Node* capacity = 6033 TaggedToParameter(LoadFixedArrayBaseLength(elements), mode); 6034 Node* new_elements = GrowElementsCapacity(object, elements, kind, kind, 6035 length, capacity, mode, bailout); 6036 6037 new_elements_var.Bind(new_elements); 6038 Goto(&done); 6039 } 6040 6041 Bind(&done); 6042 return new_elements_var.value(); 6043 } 6044 6045 void CodeStubAssembler::TransitionElementsKind(Node* object, Node* map, 6046 ElementsKind from_kind, 6047 ElementsKind to_kind, 6048 bool is_jsarray, 6049 Label* bailout) { 6050 DCHECK(!IsFastHoleyElementsKind(from_kind) || 6051 IsFastHoleyElementsKind(to_kind)); 6052 if (AllocationSite::GetMode(from_kind, to_kind) == TRACK_ALLOCATION_SITE) { 6053 TrapAllocationMemento(object, bailout); 6054 } 6055 6056 if (!IsSimpleMapChangeTransition(from_kind, to_kind)) { 6057 Comment("Non-simple map transition"); 6058 Node* elements = LoadElements(object); 6059 6060 Node* empty_fixed_array = 6061 HeapConstant(isolate()->factory()->empty_fixed_array()); 6062 6063 Label done(this); 6064 GotoIf(WordEqual(elements, empty_fixed_array), &done); 6065 6066 // TODO(ishell): Use OptimalParameterMode(). 6067 ParameterMode mode = INTPTR_PARAMETERS; 6068 Node* elements_length = SmiUntag(LoadFixedArrayBaseLength(elements)); 6069 Node* array_length = 6070 is_jsarray ? SmiUntag(LoadObjectField(object, JSArray::kLengthOffset)) 6071 : elements_length; 6072 6073 GrowElementsCapacity(object, elements, from_kind, to_kind, array_length, 6074 elements_length, mode, bailout); 6075 Goto(&done); 6076 Bind(&done); 6077 } 6078 6079 StoreMap(object, map); 6080 } 6081 6082 void CodeStubAssembler::TrapAllocationMemento(Node* object, 6083 Label* memento_found) { 6084 Comment("[ TrapAllocationMemento"); 6085 Label no_memento_found(this); 6086 Label top_check(this), map_check(this); 6087 6088 Node* new_space_top_address = ExternalConstant( 6089 ExternalReference::new_space_allocation_top_address(isolate())); 6090 const int kMementoMapOffset = JSArray::kSize; 6091 const int kMementoLastWordOffset = 6092 kMementoMapOffset + AllocationMemento::kSize - kPointerSize; 6093 6094 // Bail out if the object is not in new space. 6095 Node* object_word = BitcastTaggedToWord(object); 6096 Node* object_page = PageFromAddress(object_word); 6097 { 6098 Node* page_flags = Load(MachineType::IntPtr(), object_page, 6099 IntPtrConstant(Page::kFlagsOffset)); 6100 GotoIf(WordEqual(WordAnd(page_flags, 6101 IntPtrConstant(MemoryChunk::kIsInNewSpaceMask)), 6102 IntPtrConstant(0)), 6103 &no_memento_found); 6104 } 6105 6106 Node* memento_last_word = IntPtrAdd( 6107 object_word, IntPtrConstant(kMementoLastWordOffset - kHeapObjectTag)); 6108 Node* memento_last_word_page = PageFromAddress(memento_last_word); 6109 6110 Node* new_space_top = Load(MachineType::Pointer(), new_space_top_address); 6111 Node* new_space_top_page = PageFromAddress(new_space_top); 6112 6113 // If the object is in new space, we need to check whether respective 6114 // potential memento object is on the same page as the current top. 6115 GotoIf(WordEqual(memento_last_word_page, new_space_top_page), &top_check); 6116 6117 // The object is on a different page than allocation top. Bail out if the 6118 // object sits on the page boundary as no memento can follow and we cannot 6119 // touch the memory following it. 6120 Branch(WordEqual(object_page, memento_last_word_page), &map_check, 6121 &no_memento_found); 6122 6123 // If top is on the same page as the current object, we need to check whether 6124 // we are below top. 6125 Bind(&top_check); 6126 { 6127 Branch(UintPtrGreaterThanOrEqual(memento_last_word, new_space_top), 6128 &no_memento_found, &map_check); 6129 } 6130 6131 // Memento map check. 6132 Bind(&map_check); 6133 { 6134 Node* memento_map = LoadObjectField(object, kMementoMapOffset); 6135 Branch( 6136 WordEqual(memento_map, LoadRoot(Heap::kAllocationMementoMapRootIndex)), 6137 memento_found, &no_memento_found); 6138 } 6139 Bind(&no_memento_found); 6140 Comment("] TrapAllocationMemento"); 6141 } 6142 6143 Node* CodeStubAssembler::PageFromAddress(Node* address) { 6144 return WordAnd(address, IntPtrConstant(~Page::kPageAlignmentMask)); 6145 } 6146 6147 Node* CodeStubAssembler::EnumLength(Node* map) { 6148 CSA_ASSERT(this, IsMap(map)); 6149 Node* bitfield_3 = LoadMapBitField3(map); 6150 Node* enum_length = DecodeWordFromWord32<Map::EnumLengthBits>(bitfield_3); 6151 return SmiTag(enum_length); 6152 } 6153 6154 void CodeStubAssembler::CheckEnumCache(Node* receiver, Label* use_cache, 6155 Label* use_runtime) { 6156 Variable current_js_object(this, MachineRepresentation::kTagged, receiver); 6157 6158 Variable current_map(this, MachineRepresentation::kTagged, 6159 LoadMap(current_js_object.value())); 6160 6161 // These variables are updated in the loop below. 6162 Variable* loop_vars[2] = {¤t_js_object, ¤t_map}; 6163 Label loop(this, 2, loop_vars), next(this); 6164 6165 // Check if the enum length field is properly initialized, indicating that 6166 // there is an enum cache. 6167 { 6168 Node* invalid_enum_cache_sentinel = 6169 SmiConstant(Smi::FromInt(kInvalidEnumCacheSentinel)); 6170 Node* enum_length = EnumLength(current_map.value()); 6171 Branch(WordEqual(enum_length, invalid_enum_cache_sentinel), use_runtime, 6172 &loop); 6173 } 6174 6175 // Check that there are no elements. |current_js_object| contains 6176 // the current JS object we've reached through the prototype chain. 6177 Bind(&loop); 6178 { 6179 Label if_elements(this), if_no_elements(this); 6180 Node* elements = LoadElements(current_js_object.value()); 6181 Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex); 6182 // Check that there are no elements. 6183 Branch(WordEqual(elements, empty_fixed_array), &if_no_elements, 6184 &if_elements); 6185 Bind(&if_elements); 6186 { 6187 // Second chance, the object may be using the empty slow element 6188 // dictionary. 6189 Node* slow_empty_dictionary = 6190 LoadRoot(Heap::kEmptySlowElementDictionaryRootIndex); 6191 Branch(WordNotEqual(elements, slow_empty_dictionary), use_runtime, 6192 &if_no_elements); 6193 } 6194 6195 Bind(&if_no_elements); 6196 { 6197 // Update map prototype. 6198 current_js_object.Bind(LoadMapPrototype(current_map.value())); 6199 Branch(WordEqual(current_js_object.value(), NullConstant()), use_cache, 6200 &next); 6201 } 6202 } 6203 6204 Bind(&next); 6205 { 6206 // For all objects but the receiver, check that the cache is empty. 6207 current_map.Bind(LoadMap(current_js_object.value())); 6208 Node* enum_length = EnumLength(current_map.value()); 6209 Node* zero_constant = SmiConstant(Smi::kZero); 6210 Branch(WordEqual(enum_length, zero_constant), &loop, use_runtime); 6211 } 6212 } 6213 6214 Node* CodeStubAssembler::CreateAllocationSiteInFeedbackVector( 6215 Node* feedback_vector, Node* slot) { 6216 Node* size = IntPtrConstant(AllocationSite::kSize); 6217 Node* site = Allocate(size, CodeStubAssembler::kPretenured); 6218 6219 StoreMap(site, AllocationSiteMapConstant()); 6220 Node* kind = SmiConstant(GetInitialFastElementsKind()); 6221 StoreObjectFieldNoWriteBarrier(site, AllocationSite::kTransitionInfoOffset, 6222 kind); 6223 6224 // Unlike literals, constructed arrays don't have nested sites 6225 Node* zero = SmiConstant(0); 6226 StoreObjectFieldNoWriteBarrier(site, AllocationSite::kNestedSiteOffset, zero); 6227 6228 // Pretenuring calculation field. 6229 StoreObjectFieldNoWriteBarrier(site, AllocationSite::kPretenureDataOffset, 6230 zero); 6231 6232 // Pretenuring memento creation count field. 6233 StoreObjectFieldNoWriteBarrier( 6234 site, AllocationSite::kPretenureCreateCountOffset, zero); 6235 6236 // Store an empty fixed array for the code dependency. 6237 StoreObjectFieldRoot(site, AllocationSite::kDependentCodeOffset, 6238 Heap::kEmptyFixedArrayRootIndex); 6239 6240 // Link the object to the allocation site list 6241 Node* site_list = ExternalConstant( 6242 ExternalReference::allocation_sites_list_address(isolate())); 6243 Node* next_site = LoadBufferObject(site_list, 0); 6244 6245 // TODO(mvstanton): This is a store to a weak pointer, which we may want to 6246 // mark as such in order to skip the write barrier, once we have a unified 6247 // system for weakness. For now we decided to keep it like this because having 6248 // an initial write barrier backed store makes this pointer strong until the 6249 // next GC, and allocation sites are designed to survive several GCs anyway. 6250 StoreObjectField(site, AllocationSite::kWeakNextOffset, next_site); 6251 StoreNoWriteBarrier(MachineRepresentation::kTagged, site_list, site); 6252 6253 StoreFixedArrayElement(feedback_vector, slot, site, UPDATE_WRITE_BARRIER, 0, 6254 CodeStubAssembler::SMI_PARAMETERS); 6255 return site; 6256 } 6257 6258 Node* CodeStubAssembler::CreateWeakCellInFeedbackVector(Node* feedback_vector, 6259 Node* slot, 6260 Node* value) { 6261 Node* size = IntPtrConstant(WeakCell::kSize); 6262 Node* cell = Allocate(size, CodeStubAssembler::kPretenured); 6263 6264 // Initialize the WeakCell. 6265 DCHECK(Heap::RootIsImmortalImmovable(Heap::kWeakCellMapRootIndex)); 6266 StoreMapNoWriteBarrier(cell, Heap::kWeakCellMapRootIndex); 6267 StoreObjectField(cell, WeakCell::kValueOffset, value); 6268 StoreObjectFieldRoot(cell, WeakCell::kNextOffset, 6269 Heap::kTheHoleValueRootIndex); 6270 6271 // Store the WeakCell in the feedback vector. 6272 StoreFixedArrayElement(feedback_vector, slot, cell, UPDATE_WRITE_BARRIER, 0, 6273 CodeStubAssembler::SMI_PARAMETERS); 6274 return cell; 6275 } 6276 6277 Node* CodeStubAssembler::BuildFastLoop( 6278 const CodeStubAssembler::VariableList& vars, Node* start_index, 6279 Node* end_index, const FastLoopBody& body, int increment, 6280 ParameterMode parameter_mode, IndexAdvanceMode advance_mode) { 6281 MachineRepresentation index_rep = (parameter_mode == INTPTR_PARAMETERS) 6282 ? MachineType::PointerRepresentation() 6283 : MachineRepresentation::kTaggedSigned; 6284 Variable var(this, index_rep, start_index); 6285 VariableList vars_copy(vars, zone()); 6286 vars_copy.Add(&var, zone()); 6287 Label loop(this, vars_copy); 6288 Label after_loop(this); 6289 // Introduce an explicit second check of the termination condition before the 6290 // loop that helps turbofan generate better code. If there's only a single 6291 // check, then the CodeStubAssembler forces it to be at the beginning of the 6292 // loop requiring a backwards branch at the end of the loop (it's not possible 6293 // to force the loop header check at the end of the loop and branch forward to 6294 // it from the pre-header). The extra branch is slower in the case that the 6295 // loop actually iterates. 6296 Branch(WordEqual(var.value(), end_index), &after_loop, &loop); 6297 Bind(&loop); 6298 { 6299 if (advance_mode == IndexAdvanceMode::kPre) { 6300 Increment(var, increment, parameter_mode); 6301 } 6302 body(var.value()); 6303 if (advance_mode == IndexAdvanceMode::kPost) { 6304 Increment(var, increment, parameter_mode); 6305 } 6306 Branch(WordNotEqual(var.value(), end_index), &loop, &after_loop); 6307 } 6308 Bind(&after_loop); 6309 return var.value(); 6310 } 6311 6312 void CodeStubAssembler::BuildFastFixedArrayForEach( 6313 const CodeStubAssembler::VariableList& vars, Node* fixed_array, 6314 ElementsKind kind, Node* first_element_inclusive, 6315 Node* last_element_exclusive, const FastFixedArrayForEachBody& body, 6316 ParameterMode mode, ForEachDirection direction) { 6317 STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize); 6318 int32_t first_val; 6319 bool constant_first = ToInt32Constant(first_element_inclusive, first_val); 6320 int32_t last_val; 6321 bool constent_last = ToInt32Constant(last_element_exclusive, last_val); 6322 if (constant_first && constent_last) { 6323 int delta = last_val - first_val; 6324 DCHECK(delta >= 0); 6325 if (delta <= kElementLoopUnrollThreshold) { 6326 if (direction == ForEachDirection::kForward) { 6327 for (int i = first_val; i < last_val; ++i) { 6328 Node* index = IntPtrConstant(i); 6329 Node* offset = 6330 ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS, 6331 FixedArray::kHeaderSize - kHeapObjectTag); 6332 body(fixed_array, offset); 6333 } 6334 } else { 6335 for (int i = last_val - 1; i >= first_val; --i) { 6336 Node* index = IntPtrConstant(i); 6337 Node* offset = 6338 ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS, 6339 FixedArray::kHeaderSize - kHeapObjectTag); 6340 body(fixed_array, offset); 6341 } 6342 } 6343 return; 6344 } 6345 } 6346 6347 Node* start = 6348 ElementOffsetFromIndex(first_element_inclusive, kind, mode, 6349 FixedArray::kHeaderSize - kHeapObjectTag); 6350 Node* limit = 6351 ElementOffsetFromIndex(last_element_exclusive, kind, mode, 6352 FixedArray::kHeaderSize - kHeapObjectTag); 6353 if (direction == ForEachDirection::kReverse) std::swap(start, limit); 6354 6355 int increment = IsFastDoubleElementsKind(kind) ? kDoubleSize : kPointerSize; 6356 BuildFastLoop( 6357 vars, start, limit, 6358 [fixed_array, &body](Node* offset) { body(fixed_array, offset); }, 6359 direction == ForEachDirection::kReverse ? -increment : increment, 6360 INTPTR_PARAMETERS, 6361 direction == ForEachDirection::kReverse ? IndexAdvanceMode::kPre 6362 : IndexAdvanceMode::kPost); 6363 } 6364 6365 void CodeStubAssembler::GotoIfFixedArraySizeDoesntFitInNewSpace( 6366 Node* element_count, Label* doesnt_fit, int base_size, ParameterMode mode) { 6367 int max_newspace_parameters = 6368 (kMaxRegularHeapObjectSize - base_size) / kPointerSize; 6369 GotoIf(IntPtrOrSmiGreaterThan( 6370 element_count, IntPtrOrSmiConstant(max_newspace_parameters, mode), 6371 mode), 6372 doesnt_fit); 6373 } 6374 6375 void CodeStubAssembler::InitializeFieldsWithRoot( 6376 Node* object, Node* start_offset, Node* end_offset, 6377 Heap::RootListIndex root_index) { 6378 start_offset = IntPtrAdd(start_offset, IntPtrConstant(-kHeapObjectTag)); 6379 end_offset = IntPtrAdd(end_offset, IntPtrConstant(-kHeapObjectTag)); 6380 Node* root_value = LoadRoot(root_index); 6381 BuildFastLoop(end_offset, start_offset, 6382 [this, object, root_value](Node* current) { 6383 StoreNoWriteBarrier(MachineRepresentation::kTagged, object, 6384 current, root_value); 6385 }, 6386 -kPointerSize, INTPTR_PARAMETERS, 6387 CodeStubAssembler::IndexAdvanceMode::kPre); 6388 } 6389 6390 void CodeStubAssembler::BranchIfNumericRelationalComparison( 6391 RelationalComparisonMode mode, Node* lhs, Node* rhs, Label* if_true, 6392 Label* if_false) { 6393 Label end(this); 6394 Variable result(this, MachineRepresentation::kTagged); 6395 6396 // Shared entry for floating point comparison. 6397 Label do_fcmp(this); 6398 Variable var_fcmp_lhs(this, MachineRepresentation::kFloat64), 6399 var_fcmp_rhs(this, MachineRepresentation::kFloat64); 6400 6401 // Check if the {lhs} is a Smi or a HeapObject. 6402 Label if_lhsissmi(this), if_lhsisnotsmi(this); 6403 Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); 6404 6405 Bind(&if_lhsissmi); 6406 { 6407 // Check if {rhs} is a Smi or a HeapObject. 6408 Label if_rhsissmi(this), if_rhsisnotsmi(this); 6409 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 6410 6411 Bind(&if_rhsissmi); 6412 { 6413 // Both {lhs} and {rhs} are Smi, so just perform a fast Smi comparison. 6414 switch (mode) { 6415 case kLessThan: 6416 BranchIfSmiLessThan(lhs, rhs, if_true, if_false); 6417 break; 6418 case kLessThanOrEqual: 6419 BranchIfSmiLessThanOrEqual(lhs, rhs, if_true, if_false); 6420 break; 6421 case kGreaterThan: 6422 BranchIfSmiLessThan(rhs, lhs, if_true, if_false); 6423 break; 6424 case kGreaterThanOrEqual: 6425 BranchIfSmiLessThanOrEqual(rhs, lhs, if_true, if_false); 6426 break; 6427 } 6428 } 6429 6430 Bind(&if_rhsisnotsmi); 6431 { 6432 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(rhs))); 6433 // Convert the {lhs} and {rhs} to floating point values, and 6434 // perform a floating point comparison. 6435 var_fcmp_lhs.Bind(SmiToFloat64(lhs)); 6436 var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); 6437 Goto(&do_fcmp); 6438 } 6439 } 6440 6441 Bind(&if_lhsisnotsmi); 6442 { 6443 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(lhs))); 6444 6445 // Check if {rhs} is a Smi or a HeapObject. 6446 Label if_rhsissmi(this), if_rhsisnotsmi(this); 6447 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 6448 6449 Bind(&if_rhsissmi); 6450 { 6451 // Convert the {lhs} and {rhs} to floating point values, and 6452 // perform a floating point comparison. 6453 var_fcmp_lhs.Bind(LoadHeapNumberValue(lhs)); 6454 var_fcmp_rhs.Bind(SmiToFloat64(rhs)); 6455 Goto(&do_fcmp); 6456 } 6457 6458 Bind(&if_rhsisnotsmi); 6459 { 6460 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(rhs))); 6461 6462 // Convert the {lhs} and {rhs} to floating point values, and 6463 // perform a floating point comparison. 6464 var_fcmp_lhs.Bind(LoadHeapNumberValue(lhs)); 6465 var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); 6466 Goto(&do_fcmp); 6467 } 6468 } 6469 6470 Bind(&do_fcmp); 6471 { 6472 // Load the {lhs} and {rhs} floating point values. 6473 Node* lhs = var_fcmp_lhs.value(); 6474 Node* rhs = var_fcmp_rhs.value(); 6475 6476 // Perform a fast floating point comparison. 6477 switch (mode) { 6478 case kLessThan: 6479 Branch(Float64LessThan(lhs, rhs), if_true, if_false); 6480 break; 6481 case kLessThanOrEqual: 6482 Branch(Float64LessThanOrEqual(lhs, rhs), if_true, if_false); 6483 break; 6484 case kGreaterThan: 6485 Branch(Float64GreaterThan(lhs, rhs), if_true, if_false); 6486 break; 6487 case kGreaterThanOrEqual: 6488 Branch(Float64GreaterThanOrEqual(lhs, rhs), if_true, if_false); 6489 break; 6490 } 6491 } 6492 } 6493 6494 void CodeStubAssembler::GotoUnlessNumberLessThan(Node* lhs, Node* rhs, 6495 Label* if_false) { 6496 Label if_true(this); 6497 BranchIfNumericRelationalComparison(kLessThan, lhs, rhs, &if_true, if_false); 6498 Bind(&if_true); 6499 } 6500 6501 Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, 6502 Node* lhs, Node* rhs, 6503 Node* context) { 6504 Label return_true(this), return_false(this), end(this); 6505 Variable result(this, MachineRepresentation::kTagged); 6506 6507 // Shared entry for floating point comparison. 6508 Label do_fcmp(this); 6509 Variable var_fcmp_lhs(this, MachineRepresentation::kFloat64), 6510 var_fcmp_rhs(this, MachineRepresentation::kFloat64); 6511 6512 // We might need to loop several times due to ToPrimitive and/or ToNumber 6513 // conversions. 6514 Variable var_lhs(this, MachineRepresentation::kTagged, lhs), 6515 var_rhs(this, MachineRepresentation::kTagged, rhs); 6516 Variable* loop_vars[2] = {&var_lhs, &var_rhs}; 6517 Label loop(this, 2, loop_vars); 6518 Goto(&loop); 6519 Bind(&loop); 6520 { 6521 // Load the current {lhs} and {rhs} values. 6522 lhs = var_lhs.value(); 6523 rhs = var_rhs.value(); 6524 6525 // Check if the {lhs} is a Smi or a HeapObject. 6526 Label if_lhsissmi(this), if_lhsisnotsmi(this); 6527 Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); 6528 6529 Bind(&if_lhsissmi); 6530 { 6531 // Check if {rhs} is a Smi or a HeapObject. 6532 Label if_rhsissmi(this), if_rhsisnotsmi(this); 6533 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 6534 6535 Bind(&if_rhsissmi); 6536 { 6537 // Both {lhs} and {rhs} are Smi, so just perform a fast Smi comparison. 6538 switch (mode) { 6539 case kLessThan: 6540 BranchIfSmiLessThan(lhs, rhs, &return_true, &return_false); 6541 break; 6542 case kLessThanOrEqual: 6543 BranchIfSmiLessThanOrEqual(lhs, rhs, &return_true, &return_false); 6544 break; 6545 case kGreaterThan: 6546 BranchIfSmiLessThan(rhs, lhs, &return_true, &return_false); 6547 break; 6548 case kGreaterThanOrEqual: 6549 BranchIfSmiLessThanOrEqual(rhs, lhs, &return_true, &return_false); 6550 break; 6551 } 6552 } 6553 6554 Bind(&if_rhsisnotsmi); 6555 { 6556 // Load the map of {rhs}. 6557 Node* rhs_map = LoadMap(rhs); 6558 6559 // Check if the {rhs} is a HeapNumber. 6560 Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred); 6561 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); 6562 6563 Bind(&if_rhsisnumber); 6564 { 6565 // Convert the {lhs} and {rhs} to floating point values, and 6566 // perform a floating point comparison. 6567 var_fcmp_lhs.Bind(SmiToFloat64(lhs)); 6568 var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); 6569 Goto(&do_fcmp); 6570 } 6571 6572 Bind(&if_rhsisnotnumber); 6573 { 6574 // Convert the {rhs} to a Number; we don't need to perform the 6575 // dedicated ToPrimitive(rhs, hint Number) operation, as the 6576 // ToNumber(rhs) will by itself already invoke ToPrimitive with 6577 // a Number hint. 6578 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 6579 var_rhs.Bind(CallStub(callable, context, rhs)); 6580 Goto(&loop); 6581 } 6582 } 6583 } 6584 6585 Bind(&if_lhsisnotsmi); 6586 { 6587 // Load the map of {lhs}. 6588 Node* lhs_map = LoadMap(lhs); 6589 6590 // Check if {rhs} is a Smi or a HeapObject. 6591 Label if_rhsissmi(this), if_rhsisnotsmi(this); 6592 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 6593 6594 Bind(&if_rhsissmi); 6595 { 6596 // Check if the {lhs} is a HeapNumber. 6597 Label if_lhsisnumber(this), if_lhsisnotnumber(this, Label::kDeferred); 6598 Branch(IsHeapNumberMap(lhs_map), &if_lhsisnumber, &if_lhsisnotnumber); 6599 6600 Bind(&if_lhsisnumber); 6601 { 6602 // Convert the {lhs} and {rhs} to floating point values, and 6603 // perform a floating point comparison. 6604 var_fcmp_lhs.Bind(LoadHeapNumberValue(lhs)); 6605 var_fcmp_rhs.Bind(SmiToFloat64(rhs)); 6606 Goto(&do_fcmp); 6607 } 6608 6609 Bind(&if_lhsisnotnumber); 6610 { 6611 // Convert the {lhs} to a Number; we don't need to perform the 6612 // dedicated ToPrimitive(lhs, hint Number) operation, as the 6613 // ToNumber(lhs) will by itself already invoke ToPrimitive with 6614 // a Number hint. 6615 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 6616 var_lhs.Bind(CallStub(callable, context, lhs)); 6617 Goto(&loop); 6618 } 6619 } 6620 6621 Bind(&if_rhsisnotsmi); 6622 { 6623 // Load the map of {rhs}. 6624 Node* rhs_map = LoadMap(rhs); 6625 6626 // Check if {lhs} is a HeapNumber. 6627 Label if_lhsisnumber(this), if_lhsisnotnumber(this); 6628 Branch(IsHeapNumberMap(lhs_map), &if_lhsisnumber, &if_lhsisnotnumber); 6629 6630 Bind(&if_lhsisnumber); 6631 { 6632 // Check if {rhs} is also a HeapNumber. 6633 Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred); 6634 Branch(WordEqual(lhs_map, rhs_map), &if_rhsisnumber, 6635 &if_rhsisnotnumber); 6636 6637 Bind(&if_rhsisnumber); 6638 { 6639 // Convert the {lhs} and {rhs} to floating point values, and 6640 // perform a floating point comparison. 6641 var_fcmp_lhs.Bind(LoadHeapNumberValue(lhs)); 6642 var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); 6643 Goto(&do_fcmp); 6644 } 6645 6646 Bind(&if_rhsisnotnumber); 6647 { 6648 // Convert the {rhs} to a Number; we don't need to perform 6649 // dedicated ToPrimitive(rhs, hint Number) operation, as the 6650 // ToNumber(rhs) will by itself already invoke ToPrimitive with 6651 // a Number hint. 6652 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 6653 var_rhs.Bind(CallStub(callable, context, rhs)); 6654 Goto(&loop); 6655 } 6656 } 6657 6658 Bind(&if_lhsisnotnumber); 6659 { 6660 // Load the instance type of {lhs}. 6661 Node* lhs_instance_type = LoadMapInstanceType(lhs_map); 6662 6663 // Check if {lhs} is a String. 6664 Label if_lhsisstring(this), if_lhsisnotstring(this, Label::kDeferred); 6665 Branch(IsStringInstanceType(lhs_instance_type), &if_lhsisstring, 6666 &if_lhsisnotstring); 6667 6668 Bind(&if_lhsisstring); 6669 { 6670 // Load the instance type of {rhs}. 6671 Node* rhs_instance_type = LoadMapInstanceType(rhs_map); 6672 6673 // Check if {rhs} is also a String. 6674 Label if_rhsisstring(this, Label::kDeferred), 6675 if_rhsisnotstring(this, Label::kDeferred); 6676 Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, 6677 &if_rhsisnotstring); 6678 6679 Bind(&if_rhsisstring); 6680 { 6681 // Both {lhs} and {rhs} are strings. 6682 switch (mode) { 6683 case kLessThan: 6684 result.Bind(CallStub(CodeFactory::StringLessThan(isolate()), 6685 context, lhs, rhs)); 6686 Goto(&end); 6687 break; 6688 case kLessThanOrEqual: 6689 result.Bind( 6690 CallStub(CodeFactory::StringLessThanOrEqual(isolate()), 6691 context, lhs, rhs)); 6692 Goto(&end); 6693 break; 6694 case kGreaterThan: 6695 result.Bind( 6696 CallStub(CodeFactory::StringGreaterThan(isolate()), 6697 context, lhs, rhs)); 6698 Goto(&end); 6699 break; 6700 case kGreaterThanOrEqual: 6701 result.Bind( 6702 CallStub(CodeFactory::StringGreaterThanOrEqual(isolate()), 6703 context, lhs, rhs)); 6704 Goto(&end); 6705 break; 6706 } 6707 } 6708 6709 Bind(&if_rhsisnotstring); 6710 { 6711 // The {lhs} is a String, while {rhs} is neither a Number nor a 6712 // String, so we need to call ToPrimitive(rhs, hint Number) if 6713 // {rhs} is a receiver or ToNumber(lhs) and ToNumber(rhs) in the 6714 // other cases. 6715 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 6716 Label if_rhsisreceiver(this, Label::kDeferred), 6717 if_rhsisnotreceiver(this, Label::kDeferred); 6718 Branch(IsJSReceiverInstanceType(rhs_instance_type), 6719 &if_rhsisreceiver, &if_rhsisnotreceiver); 6720 6721 Bind(&if_rhsisreceiver); 6722 { 6723 // Convert {rhs} to a primitive first passing Number hint. 6724 Callable callable = CodeFactory::NonPrimitiveToPrimitive( 6725 isolate(), ToPrimitiveHint::kNumber); 6726 var_rhs.Bind(CallStub(callable, context, rhs)); 6727 Goto(&loop); 6728 } 6729 6730 Bind(&if_rhsisnotreceiver); 6731 { 6732 // Convert both {lhs} and {rhs} to Number. 6733 Callable callable = CodeFactory::ToNumber(isolate()); 6734 var_lhs.Bind(CallStub(callable, context, lhs)); 6735 var_rhs.Bind(CallStub(callable, context, rhs)); 6736 Goto(&loop); 6737 } 6738 } 6739 } 6740 6741 Bind(&if_lhsisnotstring); 6742 { 6743 // The {lhs} is neither a Number nor a String, so we need to call 6744 // ToPrimitive(lhs, hint Number) if {lhs} is a receiver or 6745 // ToNumber(lhs) and ToNumber(rhs) in the other cases. 6746 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 6747 Label if_lhsisreceiver(this, Label::kDeferred), 6748 if_lhsisnotreceiver(this, Label::kDeferred); 6749 Branch(IsJSReceiverInstanceType(lhs_instance_type), 6750 &if_lhsisreceiver, &if_lhsisnotreceiver); 6751 6752 Bind(&if_lhsisreceiver); 6753 { 6754 // Convert {lhs} to a primitive first passing Number hint. 6755 Callable callable = CodeFactory::NonPrimitiveToPrimitive( 6756 isolate(), ToPrimitiveHint::kNumber); 6757 var_lhs.Bind(CallStub(callable, context, lhs)); 6758 Goto(&loop); 6759 } 6760 6761 Bind(&if_lhsisnotreceiver); 6762 { 6763 // Convert both {lhs} and {rhs} to Number. 6764 Callable callable = CodeFactory::ToNumber(isolate()); 6765 var_lhs.Bind(CallStub(callable, context, lhs)); 6766 var_rhs.Bind(CallStub(callable, context, rhs)); 6767 Goto(&loop); 6768 } 6769 } 6770 } 6771 } 6772 } 6773 } 6774 6775 Bind(&do_fcmp); 6776 { 6777 // Load the {lhs} and {rhs} floating point values. 6778 Node* lhs = var_fcmp_lhs.value(); 6779 Node* rhs = var_fcmp_rhs.value(); 6780 6781 // Perform a fast floating point comparison. 6782 switch (mode) { 6783 case kLessThan: 6784 Branch(Float64LessThan(lhs, rhs), &return_true, &return_false); 6785 break; 6786 case kLessThanOrEqual: 6787 Branch(Float64LessThanOrEqual(lhs, rhs), &return_true, &return_false); 6788 break; 6789 case kGreaterThan: 6790 Branch(Float64GreaterThan(lhs, rhs), &return_true, &return_false); 6791 break; 6792 case kGreaterThanOrEqual: 6793 Branch(Float64GreaterThanOrEqual(lhs, rhs), &return_true, 6794 &return_false); 6795 break; 6796 } 6797 } 6798 6799 Bind(&return_true); 6800 { 6801 result.Bind(BooleanConstant(true)); 6802 Goto(&end); 6803 } 6804 6805 Bind(&return_false); 6806 { 6807 result.Bind(BooleanConstant(false)); 6808 Goto(&end); 6809 } 6810 6811 Bind(&end); 6812 return result.value(); 6813 } 6814 6815 namespace { 6816 6817 void GenerateEqual_Same(CodeStubAssembler* assembler, Node* value, 6818 CodeStubAssembler::Label* if_equal, 6819 CodeStubAssembler::Label* if_notequal) { 6820 // In case of abstract or strict equality checks, we need additional checks 6821 // for NaN values because they are not considered equal, even if both the 6822 // left and the right hand side reference exactly the same value. 6823 6824 typedef CodeStubAssembler::Label Label; 6825 6826 // Check if {value} is a Smi or a HeapObject. 6827 Label if_valueissmi(assembler), if_valueisnotsmi(assembler); 6828 assembler->Branch(assembler->TaggedIsSmi(value), &if_valueissmi, 6829 &if_valueisnotsmi); 6830 6831 assembler->Bind(&if_valueisnotsmi); 6832 { 6833 // Load the map of {value}. 6834 Node* value_map = assembler->LoadMap(value); 6835 6836 // Check if {value} (and therefore {rhs}) is a HeapNumber. 6837 Label if_valueisnumber(assembler), if_valueisnotnumber(assembler); 6838 assembler->Branch(assembler->IsHeapNumberMap(value_map), &if_valueisnumber, 6839 &if_valueisnotnumber); 6840 6841 assembler->Bind(&if_valueisnumber); 6842 { 6843 // Convert {value} (and therefore {rhs}) to floating point value. 6844 Node* value_value = assembler->LoadHeapNumberValue(value); 6845 6846 // Check if the HeapNumber value is a NaN. 6847 assembler->BranchIfFloat64IsNaN(value_value, if_notequal, if_equal); 6848 } 6849 6850 assembler->Bind(&if_valueisnotnumber); 6851 assembler->Goto(if_equal); 6852 } 6853 6854 assembler->Bind(&if_valueissmi); 6855 assembler->Goto(if_equal); 6856 } 6857 } // namespace 6858 6859 // ES6 section 7.2.12 Abstract Equality Comparison 6860 Node* CodeStubAssembler::Equal(ResultMode mode, Node* lhs, Node* rhs, 6861 Node* context) { 6862 // This is a slightly optimized version of Object::Equals represented as 6863 // scheduled TurboFan graph utilizing the CodeStubAssembler. Whenever you 6864 // change something functionality wise in here, remember to update the 6865 // Object::Equals method as well. 6866 6867 Label if_equal(this), if_notequal(this), 6868 do_rhsstringtonumber(this, Label::kDeferred), end(this); 6869 Variable result(this, MachineRepresentation::kTagged); 6870 6871 // Shared entry for floating point comparison. 6872 Label do_fcmp(this); 6873 Variable var_fcmp_lhs(this, MachineRepresentation::kFloat64), 6874 var_fcmp_rhs(this, MachineRepresentation::kFloat64); 6875 6876 // We might need to loop several times due to ToPrimitive and/or ToNumber 6877 // conversions. 6878 Variable var_lhs(this, MachineRepresentation::kTagged, lhs), 6879 var_rhs(this, MachineRepresentation::kTagged, rhs); 6880 Variable* loop_vars[2] = {&var_lhs, &var_rhs}; 6881 Label loop(this, 2, loop_vars); 6882 Goto(&loop); 6883 Bind(&loop); 6884 { 6885 // Load the current {lhs} and {rhs} values. 6886 lhs = var_lhs.value(); 6887 rhs = var_rhs.value(); 6888 6889 // Check if {lhs} and {rhs} refer to the same object. 6890 Label if_same(this), if_notsame(this); 6891 Branch(WordEqual(lhs, rhs), &if_same, &if_notsame); 6892 6893 Bind(&if_same); 6894 { 6895 // The {lhs} and {rhs} reference the exact same value, yet we need special 6896 // treatment for HeapNumber, as NaN is not equal to NaN. 6897 GenerateEqual_Same(this, lhs, &if_equal, &if_notequal); 6898 } 6899 6900 Bind(&if_notsame); 6901 { 6902 // Check if {lhs} is a Smi or a HeapObject. 6903 Label if_lhsissmi(this), if_lhsisnotsmi(this); 6904 Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); 6905 6906 Bind(&if_lhsissmi); 6907 { 6908 // Check if {rhs} is a Smi or a HeapObject. 6909 Label if_rhsissmi(this), if_rhsisnotsmi(this); 6910 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 6911 6912 Bind(&if_rhsissmi); 6913 // We have already checked for {lhs} and {rhs} being the same value, so 6914 // if both are Smis when we get here they must not be equal. 6915 Goto(&if_notequal); 6916 6917 Bind(&if_rhsisnotsmi); 6918 { 6919 // Load the map of {rhs}. 6920 Node* rhs_map = LoadMap(rhs); 6921 6922 // Check if {rhs} is a HeapNumber. 6923 Label if_rhsisnumber(this), if_rhsisnotnumber(this); 6924 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); 6925 6926 Bind(&if_rhsisnumber); 6927 { 6928 // Convert {lhs} and {rhs} to floating point values, and 6929 // perform a floating point comparison. 6930 var_fcmp_lhs.Bind(SmiToFloat64(lhs)); 6931 var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); 6932 Goto(&do_fcmp); 6933 } 6934 6935 Bind(&if_rhsisnotnumber); 6936 { 6937 // Load the instance type of the {rhs}. 6938 Node* rhs_instance_type = LoadMapInstanceType(rhs_map); 6939 6940 // Check if the {rhs} is a String. 6941 Label if_rhsisstring(this, Label::kDeferred), 6942 if_rhsisnotstring(this); 6943 Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, 6944 &if_rhsisnotstring); 6945 6946 Bind(&if_rhsisstring); 6947 { 6948 // The {rhs} is a String and the {lhs} is a Smi; we need 6949 // to convert the {rhs} to a Number and compare the output to 6950 // the Number on the {lhs}. 6951 Goto(&do_rhsstringtonumber); 6952 } 6953 6954 Bind(&if_rhsisnotstring); 6955 { 6956 // Check if the {rhs} is a Boolean. 6957 Label if_rhsisboolean(this), if_rhsisnotboolean(this); 6958 Branch(IsBooleanMap(rhs_map), &if_rhsisboolean, 6959 &if_rhsisnotboolean); 6960 6961 Bind(&if_rhsisboolean); 6962 { 6963 // The {rhs} is a Boolean, load its number value. 6964 var_rhs.Bind(LoadObjectField(rhs, Oddball::kToNumberOffset)); 6965 Goto(&loop); 6966 } 6967 6968 Bind(&if_rhsisnotboolean); 6969 { 6970 // Check if the {rhs} is a Receiver. 6971 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 6972 Label if_rhsisreceiver(this, Label::kDeferred), 6973 if_rhsisnotreceiver(this); 6974 Branch(IsJSReceiverInstanceType(rhs_instance_type), 6975 &if_rhsisreceiver, &if_rhsisnotreceiver); 6976 6977 Bind(&if_rhsisreceiver); 6978 { 6979 // Convert {rhs} to a primitive first (passing no hint). 6980 Callable callable = 6981 CodeFactory::NonPrimitiveToPrimitive(isolate()); 6982 var_rhs.Bind(CallStub(callable, context, rhs)); 6983 Goto(&loop); 6984 } 6985 6986 Bind(&if_rhsisnotreceiver); 6987 Goto(&if_notequal); 6988 } 6989 } 6990 } 6991 } 6992 } 6993 6994 Bind(&if_lhsisnotsmi); 6995 { 6996 // Check if {rhs} is a Smi or a HeapObject. 6997 Label if_rhsissmi(this), if_rhsisnotsmi(this); 6998 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 6999 7000 Bind(&if_rhsissmi); 7001 { 7002 // The {lhs} is a HeapObject and the {rhs} is a Smi; swapping {lhs} 7003 // and {rhs} is not observable and doesn't matter for the result, so 7004 // we can just swap them and use the Smi handling above (for {lhs} 7005 // being a Smi). 7006 var_lhs.Bind(rhs); 7007 var_rhs.Bind(lhs); 7008 Goto(&loop); 7009 } 7010 7011 Bind(&if_rhsisnotsmi); 7012 { 7013 Label if_lhsisstring(this), if_lhsisnumber(this), 7014 if_lhsissymbol(this), if_lhsisoddball(this), 7015 if_lhsisreceiver(this); 7016 7017 // Both {lhs} and {rhs} are HeapObjects, load their maps 7018 // and their instance types. 7019 Node* lhs_map = LoadMap(lhs); 7020 Node* rhs_map = LoadMap(rhs); 7021 7022 // Load the instance types of {lhs} and {rhs}. 7023 Node* lhs_instance_type = LoadMapInstanceType(lhs_map); 7024 Node* rhs_instance_type = LoadMapInstanceType(rhs_map); 7025 7026 // Dispatch based on the instance type of {lhs}. 7027 size_t const kNumCases = FIRST_NONSTRING_TYPE + 3; 7028 Label* case_labels[kNumCases]; 7029 int32_t case_values[kNumCases]; 7030 for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) { 7031 case_labels[i] = new Label(this); 7032 case_values[i] = i; 7033 } 7034 case_labels[FIRST_NONSTRING_TYPE + 0] = &if_lhsisnumber; 7035 case_values[FIRST_NONSTRING_TYPE + 0] = HEAP_NUMBER_TYPE; 7036 case_labels[FIRST_NONSTRING_TYPE + 1] = &if_lhsissymbol; 7037 case_values[FIRST_NONSTRING_TYPE + 1] = SYMBOL_TYPE; 7038 case_labels[FIRST_NONSTRING_TYPE + 2] = &if_lhsisoddball; 7039 case_values[FIRST_NONSTRING_TYPE + 2] = ODDBALL_TYPE; 7040 Switch(lhs_instance_type, &if_lhsisreceiver, case_values, case_labels, 7041 arraysize(case_values)); 7042 for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) { 7043 Bind(case_labels[i]); 7044 Goto(&if_lhsisstring); 7045 delete case_labels[i]; 7046 } 7047 7048 Bind(&if_lhsisstring); 7049 { 7050 // Check if {rhs} is also a String. 7051 Label if_rhsisstring(this, Label::kDeferred), 7052 if_rhsisnotstring(this); 7053 Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, 7054 &if_rhsisnotstring); 7055 7056 Bind(&if_rhsisstring); 7057 { 7058 // Both {lhs} and {rhs} are of type String, just do the 7059 // string comparison then. 7060 Callable callable = (mode == kDontNegateResult) 7061 ? CodeFactory::StringEqual(isolate()) 7062 : CodeFactory::StringNotEqual(isolate()); 7063 result.Bind(CallStub(callable, context, lhs, rhs)); 7064 Goto(&end); 7065 } 7066 7067 Bind(&if_rhsisnotstring); 7068 { 7069 // The {lhs} is a String and the {rhs} is some other HeapObject. 7070 // Swapping {lhs} and {rhs} is not observable and doesn't matter 7071 // for the result, so we can just swap them and use the String 7072 // handling below (for {rhs} being a String). 7073 var_lhs.Bind(rhs); 7074 var_rhs.Bind(lhs); 7075 Goto(&loop); 7076 } 7077 } 7078 7079 Bind(&if_lhsisnumber); 7080 { 7081 // Check if {rhs} is also a HeapNumber. 7082 Label if_rhsisnumber(this), if_rhsisnotnumber(this); 7083 Branch(Word32Equal(lhs_instance_type, rhs_instance_type), 7084 &if_rhsisnumber, &if_rhsisnotnumber); 7085 7086 Bind(&if_rhsisnumber); 7087 { 7088 // Convert {lhs} and {rhs} to floating point values, and 7089 // perform a floating point comparison. 7090 var_fcmp_lhs.Bind(LoadHeapNumberValue(lhs)); 7091 var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); 7092 Goto(&do_fcmp); 7093 } 7094 7095 Bind(&if_rhsisnotnumber); 7096 { 7097 // The {lhs} is a Number, the {rhs} is some other HeapObject. 7098 Label if_rhsisstring(this, Label::kDeferred), 7099 if_rhsisnotstring(this); 7100 Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, 7101 &if_rhsisnotstring); 7102 7103 Bind(&if_rhsisstring); 7104 { 7105 // The {rhs} is a String and the {lhs} is a HeapNumber; we need 7106 // to convert the {rhs} to a Number and compare the output to 7107 // the Number on the {lhs}. 7108 Goto(&do_rhsstringtonumber); 7109 } 7110 7111 Bind(&if_rhsisnotstring); 7112 { 7113 // Check if the {rhs} is a JSReceiver. 7114 Label if_rhsisreceiver(this), if_rhsisnotreceiver(this); 7115 STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); 7116 Branch(IsJSReceiverInstanceType(rhs_instance_type), 7117 &if_rhsisreceiver, &if_rhsisnotreceiver); 7118 7119 Bind(&if_rhsisreceiver); 7120 { 7121 // The {lhs} is a Primitive and the {rhs} is a JSReceiver. 7122 // Swapping {lhs} and {rhs} is not observable and doesn't 7123 // matter for the result, so we can just swap them and use 7124 // the JSReceiver handling below (for {lhs} being a 7125 // JSReceiver). 7126 var_lhs.Bind(rhs); 7127 var_rhs.Bind(lhs); 7128 Goto(&loop); 7129 } 7130 7131 Bind(&if_rhsisnotreceiver); 7132 { 7133 // Check if {rhs} is a Boolean. 7134 Label if_rhsisboolean(this), if_rhsisnotboolean(this); 7135 Branch(IsBooleanMap(rhs_map), &if_rhsisboolean, 7136 &if_rhsisnotboolean); 7137 7138 Bind(&if_rhsisboolean); 7139 { 7140 // The {rhs} is a Boolean, convert it to a Smi first. 7141 var_rhs.Bind( 7142 LoadObjectField(rhs, Oddball::kToNumberOffset)); 7143 Goto(&loop); 7144 } 7145 7146 Bind(&if_rhsisnotboolean); 7147 Goto(&if_notequal); 7148 } 7149 } 7150 } 7151 } 7152 7153 Bind(&if_lhsisoddball); 7154 { 7155 // The {lhs} is an Oddball and {rhs} is some other HeapObject. 7156 Label if_lhsisboolean(this), if_lhsisnotboolean(this); 7157 Node* boolean_map = BooleanMapConstant(); 7158 Branch(WordEqual(lhs_map, boolean_map), &if_lhsisboolean, 7159 &if_lhsisnotboolean); 7160 7161 Bind(&if_lhsisboolean); 7162 { 7163 // The {lhs} is a Boolean, check if {rhs} is also a Boolean. 7164 Label if_rhsisboolean(this), if_rhsisnotboolean(this); 7165 Branch(WordEqual(rhs_map, boolean_map), &if_rhsisboolean, 7166 &if_rhsisnotboolean); 7167 7168 Bind(&if_rhsisboolean); 7169 { 7170 // Both {lhs} and {rhs} are distinct Boolean values. 7171 Goto(&if_notequal); 7172 } 7173 7174 Bind(&if_rhsisnotboolean); 7175 { 7176 // Convert the {lhs} to a Number first. 7177 var_lhs.Bind(LoadObjectField(lhs, Oddball::kToNumberOffset)); 7178 Goto(&loop); 7179 } 7180 } 7181 7182 Bind(&if_lhsisnotboolean); 7183 { 7184 // The {lhs} is either Null or Undefined; check if the {rhs} is 7185 // undetectable (i.e. either also Null or Undefined or some 7186 // undetectable JSReceiver). 7187 Node* rhs_bitfield = LoadMapBitField(rhs_map); 7188 Branch(Word32Equal( 7189 Word32And(rhs_bitfield, 7190 Int32Constant(1 << Map::kIsUndetectable)), 7191 Int32Constant(0)), 7192 &if_notequal, &if_equal); 7193 } 7194 } 7195 7196 Bind(&if_lhsissymbol); 7197 { 7198 // Check if the {rhs} is a JSReceiver. 7199 Label if_rhsisreceiver(this), if_rhsisnotreceiver(this); 7200 STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); 7201 Branch(IsJSReceiverInstanceType(rhs_instance_type), 7202 &if_rhsisreceiver, &if_rhsisnotreceiver); 7203 7204 Bind(&if_rhsisreceiver); 7205 { 7206 // The {lhs} is a Primitive and the {rhs} is a JSReceiver. 7207 // Swapping {lhs} and {rhs} is not observable and doesn't 7208 // matter for the result, so we can just swap them and use 7209 // the JSReceiver handling below (for {lhs} being a JSReceiver). 7210 var_lhs.Bind(rhs); 7211 var_rhs.Bind(lhs); 7212 Goto(&loop); 7213 } 7214 7215 Bind(&if_rhsisnotreceiver); 7216 { 7217 // The {rhs} is not a JSReceiver and also not the same Symbol 7218 // as the {lhs}, so this is equality check is considered false. 7219 Goto(&if_notequal); 7220 } 7221 } 7222 7223 Bind(&if_lhsisreceiver); 7224 { 7225 // Check if the {rhs} is also a JSReceiver. 7226 Label if_rhsisreceiver(this), if_rhsisnotreceiver(this); 7227 STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); 7228 Branch(IsJSReceiverInstanceType(rhs_instance_type), 7229 &if_rhsisreceiver, &if_rhsisnotreceiver); 7230 7231 Bind(&if_rhsisreceiver); 7232 { 7233 // Both {lhs} and {rhs} are different JSReceiver references, so 7234 // this cannot be considered equal. 7235 Goto(&if_notequal); 7236 } 7237 7238 Bind(&if_rhsisnotreceiver); 7239 { 7240 // Check if {rhs} is Null or Undefined (an undetectable check 7241 // is sufficient here, since we already know that {rhs} is not 7242 // a JSReceiver). 7243 Label if_rhsisundetectable(this), 7244 if_rhsisnotundetectable(this, Label::kDeferred); 7245 Node* rhs_bitfield = LoadMapBitField(rhs_map); 7246 Branch(Word32Equal( 7247 Word32And(rhs_bitfield, 7248 Int32Constant(1 << Map::kIsUndetectable)), 7249 Int32Constant(0)), 7250 &if_rhsisnotundetectable, &if_rhsisundetectable); 7251 7252 Bind(&if_rhsisundetectable); 7253 { 7254 // Check if {lhs} is an undetectable JSReceiver. 7255 Node* lhs_bitfield = LoadMapBitField(lhs_map); 7256 Branch(Word32Equal( 7257 Word32And(lhs_bitfield, 7258 Int32Constant(1 << Map::kIsUndetectable)), 7259 Int32Constant(0)), 7260 &if_notequal, &if_equal); 7261 } 7262 7263 Bind(&if_rhsisnotundetectable); 7264 { 7265 // The {rhs} is some Primitive different from Null and 7266 // Undefined, need to convert {lhs} to Primitive first. 7267 Callable callable = 7268 CodeFactory::NonPrimitiveToPrimitive(isolate()); 7269 var_lhs.Bind(CallStub(callable, context, lhs)); 7270 Goto(&loop); 7271 } 7272 } 7273 } 7274 } 7275 } 7276 } 7277 7278 Bind(&do_rhsstringtonumber); 7279 { 7280 Callable callable = CodeFactory::StringToNumber(isolate()); 7281 var_rhs.Bind(CallStub(callable, context, rhs)); 7282 Goto(&loop); 7283 } 7284 } 7285 7286 Bind(&do_fcmp); 7287 { 7288 // Load the {lhs} and {rhs} floating point values. 7289 Node* lhs = var_fcmp_lhs.value(); 7290 Node* rhs = var_fcmp_rhs.value(); 7291 7292 // Perform a fast floating point comparison. 7293 Branch(Float64Equal(lhs, rhs), &if_equal, &if_notequal); 7294 } 7295 7296 Bind(&if_equal); 7297 { 7298 result.Bind(BooleanConstant(mode == kDontNegateResult)); 7299 Goto(&end); 7300 } 7301 7302 Bind(&if_notequal); 7303 { 7304 result.Bind(BooleanConstant(mode == kNegateResult)); 7305 Goto(&end); 7306 } 7307 7308 Bind(&end); 7309 return result.value(); 7310 } 7311 7312 Node* CodeStubAssembler::StrictEqual(ResultMode mode, Node* lhs, Node* rhs, 7313 Node* context) { 7314 // Here's pseudo-code for the algorithm below in case of kDontNegateResult 7315 // mode; for kNegateResult mode we properly negate the result. 7316 // 7317 // if (lhs == rhs) { 7318 // if (lhs->IsHeapNumber()) return HeapNumber::cast(lhs)->value() != NaN; 7319 // return true; 7320 // } 7321 // if (!lhs->IsSmi()) { 7322 // if (lhs->IsHeapNumber()) { 7323 // if (rhs->IsSmi()) { 7324 // return Smi::cast(rhs)->value() == HeapNumber::cast(lhs)->value(); 7325 // } else if (rhs->IsHeapNumber()) { 7326 // return HeapNumber::cast(rhs)->value() == 7327 // HeapNumber::cast(lhs)->value(); 7328 // } else { 7329 // return false; 7330 // } 7331 // } else { 7332 // if (rhs->IsSmi()) { 7333 // return false; 7334 // } else { 7335 // if (lhs->IsString()) { 7336 // if (rhs->IsString()) { 7337 // return %StringEqual(lhs, rhs); 7338 // } else { 7339 // return false; 7340 // } 7341 // } else { 7342 // return false; 7343 // } 7344 // } 7345 // } 7346 // } else { 7347 // if (rhs->IsSmi()) { 7348 // return false; 7349 // } else { 7350 // if (rhs->IsHeapNumber()) { 7351 // return Smi::cast(lhs)->value() == HeapNumber::cast(rhs)->value(); 7352 // } else { 7353 // return false; 7354 // } 7355 // } 7356 // } 7357 7358 Label if_equal(this), if_notequal(this), end(this); 7359 Variable result(this, MachineRepresentation::kTagged); 7360 7361 // Check if {lhs} and {rhs} refer to the same object. 7362 Label if_same(this), if_notsame(this); 7363 Branch(WordEqual(lhs, rhs), &if_same, &if_notsame); 7364 7365 Bind(&if_same); 7366 { 7367 // The {lhs} and {rhs} reference the exact same value, yet we need special 7368 // treatment for HeapNumber, as NaN is not equal to NaN. 7369 GenerateEqual_Same(this, lhs, &if_equal, &if_notequal); 7370 } 7371 7372 Bind(&if_notsame); 7373 { 7374 // The {lhs} and {rhs} reference different objects, yet for Smi, HeapNumber 7375 // and String they can still be considered equal. 7376 7377 // Check if {lhs} is a Smi or a HeapObject. 7378 Label if_lhsissmi(this), if_lhsisnotsmi(this); 7379 Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); 7380 7381 Bind(&if_lhsisnotsmi); 7382 { 7383 // Load the map of {lhs}. 7384 Node* lhs_map = LoadMap(lhs); 7385 7386 // Check if {lhs} is a HeapNumber. 7387 Label if_lhsisnumber(this), if_lhsisnotnumber(this); 7388 Branch(IsHeapNumberMap(lhs_map), &if_lhsisnumber, &if_lhsisnotnumber); 7389 7390 Bind(&if_lhsisnumber); 7391 { 7392 // Check if {rhs} is a Smi or a HeapObject. 7393 Label if_rhsissmi(this), if_rhsisnotsmi(this); 7394 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 7395 7396 Bind(&if_rhsissmi); 7397 { 7398 // Convert {lhs} and {rhs} to floating point values. 7399 Node* lhs_value = LoadHeapNumberValue(lhs); 7400 Node* rhs_value = SmiToFloat64(rhs); 7401 7402 // Perform a floating point comparison of {lhs} and {rhs}. 7403 Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); 7404 } 7405 7406 Bind(&if_rhsisnotsmi); 7407 { 7408 // Load the map of {rhs}. 7409 Node* rhs_map = LoadMap(rhs); 7410 7411 // Check if {rhs} is also a HeapNumber. 7412 Label if_rhsisnumber(this), if_rhsisnotnumber(this); 7413 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); 7414 7415 Bind(&if_rhsisnumber); 7416 { 7417 // Convert {lhs} and {rhs} to floating point values. 7418 Node* lhs_value = LoadHeapNumberValue(lhs); 7419 Node* rhs_value = LoadHeapNumberValue(rhs); 7420 7421 // Perform a floating point comparison of {lhs} and {rhs}. 7422 Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); 7423 } 7424 7425 Bind(&if_rhsisnotnumber); 7426 Goto(&if_notequal); 7427 } 7428 } 7429 7430 Bind(&if_lhsisnotnumber); 7431 { 7432 // Check if {rhs} is a Smi or a HeapObject. 7433 Label if_rhsissmi(this), if_rhsisnotsmi(this); 7434 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 7435 7436 Bind(&if_rhsissmi); 7437 Goto(&if_notequal); 7438 7439 Bind(&if_rhsisnotsmi); 7440 { 7441 // Load the instance type of {lhs}. 7442 Node* lhs_instance_type = LoadMapInstanceType(lhs_map); 7443 7444 // Check if {lhs} is a String. 7445 Label if_lhsisstring(this), if_lhsisnotstring(this); 7446 Branch(IsStringInstanceType(lhs_instance_type), &if_lhsisstring, 7447 &if_lhsisnotstring); 7448 7449 Bind(&if_lhsisstring); 7450 { 7451 // Load the instance type of {rhs}. 7452 Node* rhs_instance_type = LoadInstanceType(rhs); 7453 7454 // Check if {rhs} is also a String. 7455 Label if_rhsisstring(this, Label::kDeferred), 7456 if_rhsisnotstring(this); 7457 Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, 7458 &if_rhsisnotstring); 7459 7460 Bind(&if_rhsisstring); 7461 { 7462 Callable callable = (mode == kDontNegateResult) 7463 ? CodeFactory::StringEqual(isolate()) 7464 : CodeFactory::StringNotEqual(isolate()); 7465 result.Bind(CallStub(callable, context, lhs, rhs)); 7466 Goto(&end); 7467 } 7468 7469 Bind(&if_rhsisnotstring); 7470 Goto(&if_notequal); 7471 } 7472 7473 Bind(&if_lhsisnotstring); 7474 Goto(&if_notequal); 7475 } 7476 } 7477 } 7478 7479 Bind(&if_lhsissmi); 7480 { 7481 // We already know that {lhs} and {rhs} are not reference equal, and {lhs} 7482 // is a Smi; so {lhs} and {rhs} can only be strictly equal if {rhs} is a 7483 // HeapNumber with an equal floating point value. 7484 7485 // Check if {rhs} is a Smi or a HeapObject. 7486 Label if_rhsissmi(this), if_rhsisnotsmi(this); 7487 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 7488 7489 Bind(&if_rhsissmi); 7490 Goto(&if_notequal); 7491 7492 Bind(&if_rhsisnotsmi); 7493 { 7494 // Load the map of the {rhs}. 7495 Node* rhs_map = LoadMap(rhs); 7496 7497 // The {rhs} could be a HeapNumber with the same value as {lhs}. 7498 Label if_rhsisnumber(this), if_rhsisnotnumber(this); 7499 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); 7500 7501 Bind(&if_rhsisnumber); 7502 { 7503 // Convert {lhs} and {rhs} to floating point values. 7504 Node* lhs_value = SmiToFloat64(lhs); 7505 Node* rhs_value = LoadHeapNumberValue(rhs); 7506 7507 // Perform a floating point comparison of {lhs} and {rhs}. 7508 Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); 7509 } 7510 7511 Bind(&if_rhsisnotnumber); 7512 Goto(&if_notequal); 7513 } 7514 } 7515 } 7516 7517 Bind(&if_equal); 7518 { 7519 result.Bind(BooleanConstant(mode == kDontNegateResult)); 7520 Goto(&end); 7521 } 7522 7523 Bind(&if_notequal); 7524 { 7525 result.Bind(BooleanConstant(mode == kNegateResult)); 7526 Goto(&end); 7527 } 7528 7529 Bind(&end); 7530 return result.value(); 7531 } 7532 7533 // ECMA#sec-samevalue 7534 // This algorithm differs from the Strict Equality Comparison Algorithm in its 7535 // treatment of signed zeroes and NaNs. 7536 Node* CodeStubAssembler::SameValue(Node* lhs, Node* rhs, Node* context) { 7537 Variable var_result(this, MachineRepresentation::kWord32); 7538 Label strict_equal(this), out(this); 7539 7540 Node* const int_false = Int32Constant(0); 7541 Node* const int_true = Int32Constant(1); 7542 7543 Label if_equal(this), if_notequal(this); 7544 Branch(WordEqual(lhs, rhs), &if_equal, &if_notequal); 7545 7546 Bind(&if_equal); 7547 { 7548 // This covers the case when {lhs} == {rhs}. We can simply return true 7549 // because SameValue considers two NaNs to be equal. 7550 7551 var_result.Bind(int_true); 7552 Goto(&out); 7553 } 7554 7555 Bind(&if_notequal); 7556 { 7557 // This covers the case when {lhs} != {rhs}. We only handle numbers here 7558 // and defer to StrictEqual for the rest. 7559 7560 Node* const lhs_float = TryTaggedToFloat64(lhs, &strict_equal); 7561 Node* const rhs_float = TryTaggedToFloat64(rhs, &strict_equal); 7562 7563 Label if_lhsisnan(this), if_lhsnotnan(this); 7564 BranchIfFloat64IsNaN(lhs_float, &if_lhsisnan, &if_lhsnotnan); 7565 7566 Bind(&if_lhsisnan); 7567 { 7568 // Return true iff {rhs} is NaN. 7569 7570 Node* const result = 7571 SelectConstant(Float64Equal(rhs_float, rhs_float), int_false, 7572 int_true, MachineRepresentation::kWord32); 7573 var_result.Bind(result); 7574 Goto(&out); 7575 } 7576 7577 Bind(&if_lhsnotnan); 7578 { 7579 Label if_floatisequal(this), if_floatnotequal(this); 7580 Branch(Float64Equal(lhs_float, rhs_float), &if_floatisequal, 7581 &if_floatnotequal); 7582 7583 Bind(&if_floatisequal); 7584 { 7585 // We still need to handle the case when {lhs} and {rhs} are -0.0 and 7586 // 0.0 (or vice versa). Compare the high word to 7587 // distinguish between the two. 7588 7589 Node* const lhs_hi_word = Float64ExtractHighWord32(lhs_float); 7590 Node* const rhs_hi_word = Float64ExtractHighWord32(rhs_float); 7591 7592 // If x is +0 and y is -0, return false. 7593 // If x is -0 and y is +0, return false. 7594 7595 Node* const result = Word32Equal(lhs_hi_word, rhs_hi_word); 7596 var_result.Bind(result); 7597 Goto(&out); 7598 } 7599 7600 Bind(&if_floatnotequal); 7601 { 7602 var_result.Bind(int_false); 7603 Goto(&out); 7604 } 7605 } 7606 } 7607 7608 Bind(&strict_equal); 7609 { 7610 Node* const is_equal = StrictEqual(kDontNegateResult, lhs, rhs, context); 7611 Node* const result = WordEqual(is_equal, TrueConstant()); 7612 var_result.Bind(result); 7613 Goto(&out); 7614 } 7615 7616 Bind(&out); 7617 return var_result.value(); 7618 } 7619 7620 Node* CodeStubAssembler::ForInFilter(Node* key, Node* object, Node* context) { 7621 Label return_undefined(this, Label::kDeferred), return_to_name(this), 7622 end(this); 7623 7624 Variable var_result(this, MachineRepresentation::kTagged); 7625 7626 Node* has_property = 7627 HasProperty(object, key, context, Runtime::kForInHasProperty); 7628 7629 Branch(WordEqual(has_property, BooleanConstant(true)), &return_to_name, 7630 &return_undefined); 7631 7632 Bind(&return_to_name); 7633 { 7634 var_result.Bind(ToName(context, key)); 7635 Goto(&end); 7636 } 7637 7638 Bind(&return_undefined); 7639 { 7640 var_result.Bind(UndefinedConstant()); 7641 Goto(&end); 7642 } 7643 7644 Bind(&end); 7645 return var_result.value(); 7646 } 7647 7648 Node* CodeStubAssembler::HasProperty( 7649 Node* object, Node* key, Node* context, 7650 Runtime::FunctionId fallback_runtime_function_id) { 7651 Label call_runtime(this, Label::kDeferred), return_true(this), 7652 return_false(this), end(this); 7653 7654 CodeStubAssembler::LookupInHolder lookup_property_in_holder = 7655 [this, &return_true](Node* receiver, Node* holder, Node* holder_map, 7656 Node* holder_instance_type, Node* unique_name, 7657 Label* next_holder, Label* if_bailout) { 7658 TryHasOwnProperty(holder, holder_map, holder_instance_type, unique_name, 7659 &return_true, next_holder, if_bailout); 7660 }; 7661 7662 CodeStubAssembler::LookupInHolder lookup_element_in_holder = 7663 [this, &return_true](Node* receiver, Node* holder, Node* holder_map, 7664 Node* holder_instance_type, Node* index, 7665 Label* next_holder, Label* if_bailout) { 7666 TryLookupElement(holder, holder_map, holder_instance_type, index, 7667 &return_true, next_holder, if_bailout); 7668 }; 7669 7670 TryPrototypeChainLookup(object, key, lookup_property_in_holder, 7671 lookup_element_in_holder, &return_false, 7672 &call_runtime); 7673 7674 Variable result(this, MachineRepresentation::kTagged); 7675 Bind(&return_true); 7676 { 7677 result.Bind(BooleanConstant(true)); 7678 Goto(&end); 7679 } 7680 7681 Bind(&return_false); 7682 { 7683 result.Bind(BooleanConstant(false)); 7684 Goto(&end); 7685 } 7686 7687 Bind(&call_runtime); 7688 { 7689 result.Bind( 7690 CallRuntime(fallback_runtime_function_id, context, object, key)); 7691 Goto(&end); 7692 } 7693 7694 Bind(&end); 7695 return result.value(); 7696 } 7697 7698 Node* CodeStubAssembler::ClassOf(Node* value) { 7699 Variable var_result(this, MachineRepresentation::kTaggedPointer); 7700 Label if_function(this, Label::kDeferred), if_object(this, Label::kDeferred), 7701 if_primitive(this, Label::kDeferred), return_result(this); 7702 7703 // Check if {value} is a Smi. 7704 GotoIf(TaggedIsSmi(value), &if_primitive); 7705 7706 Node* value_map = LoadMap(value); 7707 Node* value_instance_type = LoadMapInstanceType(value_map); 7708 7709 // Check if {value} is a JSFunction or JSBoundFunction. 7710 STATIC_ASSERT(LAST_TYPE == LAST_FUNCTION_TYPE); 7711 GotoIf(Uint32LessThanOrEqual(Int32Constant(FIRST_FUNCTION_TYPE), 7712 value_instance_type), 7713 &if_function); 7714 7715 // Check if {value} is a primitive HeapObject. 7716 STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); 7717 GotoIf(Uint32LessThan(value_instance_type, 7718 Int32Constant(FIRST_JS_RECEIVER_TYPE)), 7719 &if_primitive); 7720 7721 // Load the {value}s constructor, and check that it's a JSFunction. 7722 Node* constructor = LoadMapConstructor(value_map); 7723 GotoIfNot(IsJSFunction(constructor), &if_object); 7724 7725 // Return the instance class name for the {constructor}. 7726 Node* shared_info = 7727 LoadObjectField(constructor, JSFunction::kSharedFunctionInfoOffset); 7728 Node* instance_class_name = LoadObjectField( 7729 shared_info, SharedFunctionInfo::kInstanceClassNameOffset); 7730 var_result.Bind(instance_class_name); 7731 Goto(&return_result); 7732 7733 Bind(&if_function); 7734 var_result.Bind(LoadRoot(Heap::kFunction_stringRootIndex)); 7735 Goto(&return_result); 7736 7737 Bind(&if_object); 7738 var_result.Bind(LoadRoot(Heap::kObject_stringRootIndex)); 7739 Goto(&return_result); 7740 7741 Bind(&if_primitive); 7742 var_result.Bind(NullConstant()); 7743 Goto(&return_result); 7744 7745 Bind(&return_result); 7746 return var_result.value(); 7747 } 7748 7749 Node* CodeStubAssembler::Typeof(Node* value, Node* context) { 7750 Variable result_var(this, MachineRepresentation::kTagged); 7751 7752 Label return_number(this, Label::kDeferred), if_oddball(this), 7753 return_function(this), return_undefined(this), return_object(this), 7754 return_string(this), return_result(this); 7755 7756 GotoIf(TaggedIsSmi(value), &return_number); 7757 7758 Node* map = LoadMap(value); 7759 7760 GotoIf(IsHeapNumberMap(map), &return_number); 7761 7762 Node* instance_type = LoadMapInstanceType(map); 7763 7764 GotoIf(Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE)), &if_oddball); 7765 7766 Node* callable_or_undetectable_mask = Word32And( 7767 LoadMapBitField(map), 7768 Int32Constant(1 << Map::kIsCallable | 1 << Map::kIsUndetectable)); 7769 7770 GotoIf(Word32Equal(callable_or_undetectable_mask, 7771 Int32Constant(1 << Map::kIsCallable)), 7772 &return_function); 7773 7774 GotoIfNot(Word32Equal(callable_or_undetectable_mask, Int32Constant(0)), 7775 &return_undefined); 7776 7777 GotoIf(IsJSReceiverInstanceType(instance_type), &return_object); 7778 7779 GotoIf(IsStringInstanceType(instance_type), &return_string); 7780 7781 CSA_ASSERT(this, Word32Equal(instance_type, Int32Constant(SYMBOL_TYPE))); 7782 result_var.Bind(HeapConstant(isolate()->factory()->symbol_string())); 7783 Goto(&return_result); 7784 7785 Bind(&return_number); 7786 { 7787 result_var.Bind(HeapConstant(isolate()->factory()->number_string())); 7788 Goto(&return_result); 7789 } 7790 7791 Bind(&if_oddball); 7792 { 7793 Node* type = LoadObjectField(value, Oddball::kTypeOfOffset); 7794 result_var.Bind(type); 7795 Goto(&return_result); 7796 } 7797 7798 Bind(&return_function); 7799 { 7800 result_var.Bind(HeapConstant(isolate()->factory()->function_string())); 7801 Goto(&return_result); 7802 } 7803 7804 Bind(&return_undefined); 7805 { 7806 result_var.Bind(HeapConstant(isolate()->factory()->undefined_string())); 7807 Goto(&return_result); 7808 } 7809 7810 Bind(&return_object); 7811 { 7812 result_var.Bind(HeapConstant(isolate()->factory()->object_string())); 7813 Goto(&return_result); 7814 } 7815 7816 Bind(&return_string); 7817 { 7818 result_var.Bind(HeapConstant(isolate()->factory()->string_string())); 7819 Goto(&return_result); 7820 } 7821 7822 Bind(&return_result); 7823 return result_var.value(); 7824 } 7825 7826 Node* CodeStubAssembler::GetSuperConstructor(Node* active_function, 7827 Node* context) { 7828 CSA_ASSERT(this, IsJSFunction(active_function)); 7829 7830 Label is_not_constructor(this, Label::kDeferred), out(this); 7831 Variable result(this, MachineRepresentation::kTagged); 7832 7833 Node* map = LoadMap(active_function); 7834 Node* prototype = LoadMapPrototype(map); 7835 Node* prototype_map = LoadMap(prototype); 7836 GotoIfNot(IsConstructorMap(prototype_map), &is_not_constructor); 7837 7838 result.Bind(prototype); 7839 Goto(&out); 7840 7841 Bind(&is_not_constructor); 7842 { 7843 CallRuntime(Runtime::kThrowNotSuperConstructor, context, prototype, 7844 active_function); 7845 Unreachable(); 7846 } 7847 7848 Bind(&out); 7849 return result.value(); 7850 } 7851 7852 Node* CodeStubAssembler::InstanceOf(Node* object, Node* callable, 7853 Node* context) { 7854 Variable var_result(this, MachineRepresentation::kTagged); 7855 Label if_notcallable(this, Label::kDeferred), 7856 if_notreceiver(this, Label::kDeferred), if_otherhandler(this), 7857 if_nohandler(this, Label::kDeferred), return_true(this), 7858 return_false(this), return_result(this, &var_result); 7859 7860 // Ensure that the {callable} is actually a JSReceiver. 7861 GotoIf(TaggedIsSmi(callable), &if_notreceiver); 7862 GotoIfNot(IsJSReceiver(callable), &if_notreceiver); 7863 7864 // Load the @@hasInstance property from {callable}. 7865 Node* inst_of_handler = CallStub(CodeFactory::GetProperty(isolate()), context, 7866 callable, HasInstanceSymbolConstant()); 7867 7868 // Optimize for the likely case where {inst_of_handler} is the builtin 7869 // Function.prototype[@@hasInstance] method, and emit a direct call in 7870 // that case without any additional checking. 7871 Node* native_context = LoadNativeContext(context); 7872 Node* function_has_instance = 7873 LoadContextElement(native_context, Context::FUNCTION_HAS_INSTANCE_INDEX); 7874 GotoIfNot(WordEqual(inst_of_handler, function_has_instance), 7875 &if_otherhandler); 7876 { 7877 // Call to Function.prototype[@@hasInstance] directly. 7878 Callable builtin(isolate()->builtins()->FunctionPrototypeHasInstance(), 7879 CallTrampolineDescriptor(isolate())); 7880 Node* result = CallJS(builtin, context, inst_of_handler, callable, object); 7881 var_result.Bind(result); 7882 Goto(&return_result); 7883 } 7884 7885 Bind(&if_otherhandler); 7886 { 7887 // Check if there's actually an {inst_of_handler}. 7888 GotoIf(IsNull(inst_of_handler), &if_nohandler); 7889 GotoIf(IsUndefined(inst_of_handler), &if_nohandler); 7890 7891 // Call the {inst_of_handler} for {callable} and {object}. 7892 Node* result = CallJS( 7893 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined), 7894 context, inst_of_handler, callable, object); 7895 7896 // Convert the {result} to a Boolean. 7897 BranchIfToBooleanIsTrue(result, &return_true, &return_false); 7898 } 7899 7900 Bind(&if_nohandler); 7901 { 7902 // Ensure that the {callable} is actually Callable. 7903 GotoIfNot(IsCallable(callable), &if_notcallable); 7904 7905 // Use the OrdinaryHasInstance algorithm. 7906 Node* result = CallStub(CodeFactory::OrdinaryHasInstance(isolate()), 7907 context, callable, object); 7908 var_result.Bind(result); 7909 Goto(&return_result); 7910 } 7911 7912 Bind(&if_notcallable); 7913 { 7914 CallRuntime(Runtime::kThrowNonCallableInInstanceOfCheck, context); 7915 Unreachable(); 7916 } 7917 7918 Bind(&if_notreceiver); 7919 { 7920 CallRuntime(Runtime::kThrowNonObjectInInstanceOfCheck, context); 7921 Unreachable(); 7922 } 7923 7924 Bind(&return_true); 7925 var_result.Bind(TrueConstant()); 7926 Goto(&return_result); 7927 7928 Bind(&return_false); 7929 var_result.Bind(FalseConstant()); 7930 Goto(&return_result); 7931 7932 Bind(&return_result); 7933 return var_result.value(); 7934 } 7935 7936 Node* CodeStubAssembler::NumberInc(Node* value) { 7937 Variable var_result(this, MachineRepresentation::kTagged), 7938 var_finc_value(this, MachineRepresentation::kFloat64); 7939 Label if_issmi(this), if_isnotsmi(this), do_finc(this), end(this); 7940 Branch(TaggedIsSmi(value), &if_issmi, &if_isnotsmi); 7941 7942 Bind(&if_issmi); 7943 { 7944 // Try fast Smi addition first. 7945 Node* one = SmiConstant(Smi::FromInt(1)); 7946 Node* pair = IntPtrAddWithOverflow(BitcastTaggedToWord(value), 7947 BitcastTaggedToWord(one)); 7948 Node* overflow = Projection(1, pair); 7949 7950 // Check if the Smi addition overflowed. 7951 Label if_overflow(this), if_notoverflow(this); 7952 Branch(overflow, &if_overflow, &if_notoverflow); 7953 7954 Bind(&if_notoverflow); 7955 var_result.Bind(BitcastWordToTaggedSigned(Projection(0, pair))); 7956 Goto(&end); 7957 7958 Bind(&if_overflow); 7959 { 7960 var_finc_value.Bind(SmiToFloat64(value)); 7961 Goto(&do_finc); 7962 } 7963 } 7964 7965 Bind(&if_isnotsmi); 7966 { 7967 // Check if the value is a HeapNumber. 7968 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(value))); 7969 7970 // Load the HeapNumber value. 7971 var_finc_value.Bind(LoadHeapNumberValue(value)); 7972 Goto(&do_finc); 7973 } 7974 7975 Bind(&do_finc); 7976 { 7977 Node* finc_value = var_finc_value.value(); 7978 Node* one = Float64Constant(1.0); 7979 Node* finc_result = Float64Add(finc_value, one); 7980 var_result.Bind(AllocateHeapNumberWithValue(finc_result)); 7981 Goto(&end); 7982 } 7983 7984 Bind(&end); 7985 return var_result.value(); 7986 } 7987 7988 void CodeStubAssembler::GotoIfNotNumber(Node* input, Label* is_not_number) { 7989 Label is_number(this); 7990 GotoIf(TaggedIsSmi(input), &is_number); 7991 Node* input_map = LoadMap(input); 7992 Branch(IsHeapNumberMap(input_map), &is_number, is_not_number); 7993 Bind(&is_number); 7994 } 7995 7996 void CodeStubAssembler::GotoIfNumber(Node* input, Label* is_number) { 7997 GotoIf(TaggedIsSmi(input), is_number); 7998 Node* input_map = LoadMap(input); 7999 GotoIf(IsHeapNumberMap(input_map), is_number); 8000 } 8001 8002 Node* CodeStubAssembler::CreateArrayIterator(Node* array, Node* array_map, 8003 Node* array_type, Node* context, 8004 IterationKind mode) { 8005 int kBaseMapIndex = 0; 8006 switch (mode) { 8007 case IterationKind::kKeys: 8008 kBaseMapIndex = Context::TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX; 8009 break; 8010 case IterationKind::kValues: 8011 kBaseMapIndex = Context::UINT8_ARRAY_VALUE_ITERATOR_MAP_INDEX; 8012 break; 8013 case IterationKind::kEntries: 8014 kBaseMapIndex = Context::UINT8_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX; 8015 break; 8016 } 8017 8018 // Fast Array iterator map index: 8019 // (kBaseIndex + kFastIteratorOffset) + ElementsKind (for JSArrays) 8020 // kBaseIndex + (ElementsKind - UINT8_ELEMENTS) (for JSTypedArrays) 8021 const int kFastIteratorOffset = 8022 Context::FAST_SMI_ARRAY_VALUE_ITERATOR_MAP_INDEX - 8023 Context::UINT8_ARRAY_VALUE_ITERATOR_MAP_INDEX; 8024 STATIC_ASSERT(kFastIteratorOffset == 8025 (Context::FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX - 8026 Context::UINT8_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX)); 8027 8028 // Slow Array iterator map index: (kBaseIndex + kSlowIteratorOffset) 8029 const int kSlowIteratorOffset = 8030 Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX - 8031 Context::UINT8_ARRAY_VALUE_ITERATOR_MAP_INDEX; 8032 STATIC_ASSERT(kSlowIteratorOffset == 8033 (Context::GENERIC_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX - 8034 Context::UINT8_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX)); 8035 8036 // Assert: Type(array) is Object 8037 CSA_ASSERT(this, IsJSReceiverInstanceType(array_type)); 8038 8039 Variable var_result(this, MachineRepresentation::kTagged); 8040 Variable var_map_index(this, MachineType::PointerRepresentation()); 8041 Variable var_array_map(this, MachineRepresentation::kTagged); 8042 8043 Label return_result(this); 8044 Label allocate_iterator(this); 8045 8046 if (mode == IterationKind::kKeys) { 8047 // There are only two key iterator maps, branch depending on whether or not 8048 // the receiver is a TypedArray or not. 8049 8050 Label if_istypedarray(this), if_isgeneric(this); 8051 8052 Branch(Word32Equal(array_type, Int32Constant(JS_TYPED_ARRAY_TYPE)), 8053 &if_istypedarray, &if_isgeneric); 8054 8055 Bind(&if_isgeneric); 8056 { 8057 Label if_isfast(this), if_isslow(this); 8058 BranchIfFastJSArray(array, context, FastJSArrayAccessMode::INBOUNDS_READ, 8059 &if_isfast, &if_isslow); 8060 8061 Bind(&if_isfast); 8062 { 8063 var_map_index.Bind( 8064 IntPtrConstant(Context::FAST_ARRAY_KEY_ITERATOR_MAP_INDEX)); 8065 var_array_map.Bind(array_map); 8066 Goto(&allocate_iterator); 8067 } 8068 8069 Bind(&if_isslow); 8070 { 8071 var_map_index.Bind( 8072 IntPtrConstant(Context::GENERIC_ARRAY_KEY_ITERATOR_MAP_INDEX)); 8073 var_array_map.Bind(UndefinedConstant()); 8074 Goto(&allocate_iterator); 8075 } 8076 } 8077 8078 Bind(&if_istypedarray); 8079 { 8080 var_map_index.Bind( 8081 IntPtrConstant(Context::TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX)); 8082 var_array_map.Bind(UndefinedConstant()); 8083 Goto(&allocate_iterator); 8084 } 8085 } else { 8086 Label if_istypedarray(this), if_isgeneric(this); 8087 Branch(Word32Equal(array_type, Int32Constant(JS_TYPED_ARRAY_TYPE)), 8088 &if_istypedarray, &if_isgeneric); 8089 8090 Bind(&if_isgeneric); 8091 { 8092 Label if_isfast(this), if_isslow(this); 8093 BranchIfFastJSArray(array, context, FastJSArrayAccessMode::INBOUNDS_READ, 8094 &if_isfast, &if_isslow); 8095 8096 Bind(&if_isfast); 8097 { 8098 Label if_ispacked(this), if_isholey(this); 8099 Node* elements_kind = LoadMapElementsKind(array_map); 8100 Branch(IsHoleyFastElementsKind(elements_kind), &if_isholey, 8101 &if_ispacked); 8102 8103 Bind(&if_isholey); 8104 { 8105 // Fast holey JSArrays can treat the hole as undefined if the 8106 // protector cell is valid, and the prototype chain is unchanged from 8107 // its initial state (because the protector cell is only tracked for 8108 // initial the Array and Object prototypes). Check these conditions 8109 // here, and take the slow path if any fail. 8110 Node* protector_cell = LoadRoot(Heap::kArrayProtectorRootIndex); 8111 DCHECK(isolate()->heap()->array_protector()->IsPropertyCell()); 8112 GotoIfNot( 8113 WordEqual( 8114 LoadObjectField(protector_cell, PropertyCell::kValueOffset), 8115 SmiConstant(Smi::FromInt(Isolate::kProtectorValid))), 8116 &if_isslow); 8117 8118 Node* native_context = LoadNativeContext(context); 8119 8120 Node* prototype = LoadMapPrototype(array_map); 8121 Node* array_prototype = LoadContextElement( 8122 native_context, Context::INITIAL_ARRAY_PROTOTYPE_INDEX); 8123 GotoIfNot(WordEqual(prototype, array_prototype), &if_isslow); 8124 8125 Node* map = LoadMap(prototype); 8126 prototype = LoadMapPrototype(map); 8127 Node* object_prototype = LoadContextElement( 8128 native_context, Context::INITIAL_OBJECT_PROTOTYPE_INDEX); 8129 GotoIfNot(WordEqual(prototype, object_prototype), &if_isslow); 8130 8131 map = LoadMap(prototype); 8132 prototype = LoadMapPrototype(map); 8133 Branch(IsNull(prototype), &if_ispacked, &if_isslow); 8134 } 8135 Bind(&if_ispacked); 8136 { 8137 Node* map_index = 8138 IntPtrAdd(IntPtrConstant(kBaseMapIndex + kFastIteratorOffset), 8139 ChangeUint32ToWord(LoadMapElementsKind(array_map))); 8140 CSA_ASSERT(this, IntPtrGreaterThanOrEqual( 8141 map_index, IntPtrConstant(kBaseMapIndex + 8142 kFastIteratorOffset))); 8143 CSA_ASSERT(this, IntPtrLessThan(map_index, 8144 IntPtrConstant(kBaseMapIndex + 8145 kSlowIteratorOffset))); 8146 8147 var_map_index.Bind(map_index); 8148 var_array_map.Bind(array_map); 8149 Goto(&allocate_iterator); 8150 } 8151 } 8152 8153 Bind(&if_isslow); 8154 { 8155 Node* map_index = IntPtrAdd(IntPtrConstant(kBaseMapIndex), 8156 IntPtrConstant(kSlowIteratorOffset)); 8157 var_map_index.Bind(map_index); 8158 var_array_map.Bind(UndefinedConstant()); 8159 Goto(&allocate_iterator); 8160 } 8161 } 8162 8163 Bind(&if_istypedarray); 8164 { 8165 Node* map_index = 8166 IntPtrAdd(IntPtrConstant(kBaseMapIndex - UINT8_ELEMENTS), 8167 ChangeUint32ToWord(LoadMapElementsKind(array_map))); 8168 CSA_ASSERT( 8169 this, IntPtrLessThan(map_index, IntPtrConstant(kBaseMapIndex + 8170 kFastIteratorOffset))); 8171 CSA_ASSERT(this, IntPtrGreaterThanOrEqual(map_index, 8172 IntPtrConstant(kBaseMapIndex))); 8173 var_map_index.Bind(map_index); 8174 var_array_map.Bind(UndefinedConstant()); 8175 Goto(&allocate_iterator); 8176 } 8177 } 8178 8179 Bind(&allocate_iterator); 8180 { 8181 Node* map = LoadFixedArrayElement(LoadNativeContext(context), 8182 var_map_index.value()); 8183 var_result.Bind(AllocateJSArrayIterator(array, var_array_map.value(), map)); 8184 Goto(&return_result); 8185 } 8186 8187 Bind(&return_result); 8188 return var_result.value(); 8189 } 8190 8191 Node* CodeStubAssembler::AllocateJSArrayIterator(Node* array, Node* array_map, 8192 Node* map) { 8193 Node* iterator = Allocate(JSArrayIterator::kSize); 8194 StoreMapNoWriteBarrier(iterator, map); 8195 StoreObjectFieldRoot(iterator, JSArrayIterator::kPropertiesOffset, 8196 Heap::kEmptyFixedArrayRootIndex); 8197 StoreObjectFieldRoot(iterator, JSArrayIterator::kElementsOffset, 8198 Heap::kEmptyFixedArrayRootIndex); 8199 StoreObjectFieldNoWriteBarrier(iterator, 8200 JSArrayIterator::kIteratedObjectOffset, array); 8201 StoreObjectFieldNoWriteBarrier(iterator, JSArrayIterator::kNextIndexOffset, 8202 SmiConstant(Smi::FromInt(0))); 8203 StoreObjectFieldNoWriteBarrier( 8204 iterator, JSArrayIterator::kIteratedObjectMapOffset, array_map); 8205 return iterator; 8206 } 8207 8208 Node* CodeStubAssembler::IsDetachedBuffer(Node* buffer) { 8209 CSA_ASSERT(this, HasInstanceType(buffer, JS_ARRAY_BUFFER_TYPE)); 8210 8211 Node* buffer_bit_field = LoadObjectField( 8212 buffer, JSArrayBuffer::kBitFieldOffset, MachineType::Uint32()); 8213 return IsSetWord32<JSArrayBuffer::WasNeutered>(buffer_bit_field); 8214 } 8215 8216 CodeStubArguments::CodeStubArguments(CodeStubAssembler* assembler, Node* argc, 8217 Node* fp, 8218 CodeStubAssembler::ParameterMode mode) 8219 : assembler_(assembler), 8220 argc_mode_(mode), 8221 argc_(argc), 8222 arguments_(nullptr), 8223 fp_(fp != nullptr ? fp : assembler->LoadFramePointer()) { 8224 Node* offset = assembler->ElementOffsetFromIndex( 8225 argc_, FAST_ELEMENTS, mode, 8226 (StandardFrameConstants::kFixedSlotCountAboveFp - 1) * kPointerSize); 8227 arguments_ = assembler_->IntPtrAdd(fp_, offset); 8228 } 8229 8230 Node* CodeStubArguments::GetReceiver() const { 8231 return assembler_->Load(MachineType::AnyTagged(), arguments_, 8232 assembler_->IntPtrConstant(kPointerSize)); 8233 } 8234 8235 Node* CodeStubArguments::AtIndexPtr( 8236 Node* index, CodeStubAssembler::ParameterMode mode) const { 8237 typedef compiler::Node Node; 8238 Node* negated_index = assembler_->IntPtrOrSmiSub( 8239 assembler_->IntPtrOrSmiConstant(0, mode), index, mode); 8240 Node* offset = 8241 assembler_->ElementOffsetFromIndex(negated_index, FAST_ELEMENTS, mode, 0); 8242 return assembler_->IntPtrAdd(arguments_, offset); 8243 } 8244 8245 Node* CodeStubArguments::AtIndex(Node* index, 8246 CodeStubAssembler::ParameterMode mode) const { 8247 DCHECK_EQ(argc_mode_, mode); 8248 CSA_ASSERT(assembler_, 8249 assembler_->UintPtrOrSmiLessThan(index, GetLength(), mode)); 8250 return assembler_->Load(MachineType::AnyTagged(), AtIndexPtr(index, mode)); 8251 } 8252 8253 Node* CodeStubArguments::AtIndex(int index) const { 8254 return AtIndex(assembler_->IntPtrConstant(index)); 8255 } 8256 8257 void CodeStubArguments::ForEach( 8258 const CodeStubAssembler::VariableList& vars, 8259 const CodeStubArguments::ForEachBodyFunction& body, Node* first, Node* last, 8260 CodeStubAssembler::ParameterMode mode) { 8261 assembler_->Comment("CodeStubArguments::ForEach"); 8262 if (first == nullptr) { 8263 first = assembler_->IntPtrOrSmiConstant(0, mode); 8264 } 8265 if (last == nullptr) { 8266 DCHECK_EQ(mode, argc_mode_); 8267 last = argc_; 8268 } 8269 Node* start = assembler_->IntPtrSub( 8270 arguments_, 8271 assembler_->ElementOffsetFromIndex(first, FAST_ELEMENTS, mode)); 8272 Node* end = assembler_->IntPtrSub( 8273 arguments_, 8274 assembler_->ElementOffsetFromIndex(last, FAST_ELEMENTS, mode)); 8275 assembler_->BuildFastLoop(vars, start, end, 8276 [this, &body](Node* current) { 8277 Node* arg = assembler_->Load( 8278 MachineType::AnyTagged(), current); 8279 body(arg); 8280 }, 8281 -kPointerSize, CodeStubAssembler::INTPTR_PARAMETERS, 8282 CodeStubAssembler::IndexAdvanceMode::kPost); 8283 } 8284 8285 void CodeStubArguments::PopAndReturn(Node* value) { 8286 assembler_->PopAndReturn( 8287 assembler_->IntPtrAdd(argc_, assembler_->IntPtrConstant(1)), value); 8288 } 8289 8290 Node* CodeStubAssembler::IsFastElementsKind(Node* elements_kind) { 8291 return Uint32LessThanOrEqual(elements_kind, 8292 Int32Constant(LAST_FAST_ELEMENTS_KIND)); 8293 } 8294 8295 Node* CodeStubAssembler::IsHoleyFastElementsKind(Node* elements_kind) { 8296 CSA_ASSERT(this, IsFastElementsKind(elements_kind)); 8297 8298 STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == (FAST_SMI_ELEMENTS | 1)); 8299 STATIC_ASSERT(FAST_HOLEY_ELEMENTS == (FAST_ELEMENTS | 1)); 8300 STATIC_ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == (FAST_DOUBLE_ELEMENTS | 1)); 8301 8302 // Check prototype chain if receiver does not have packed elements. 8303 Node* holey_elements = Word32And(elements_kind, Int32Constant(1)); 8304 return Word32Equal(holey_elements, Int32Constant(1)); 8305 } 8306 8307 Node* CodeStubAssembler::IsDebugActive() { 8308 Node* is_debug_active = Load( 8309 MachineType::Uint8(), 8310 ExternalConstant(ExternalReference::debug_is_active_address(isolate()))); 8311 return Word32NotEqual(is_debug_active, Int32Constant(0)); 8312 } 8313 8314 Node* CodeStubAssembler::IsPromiseHookEnabledOrDebugIsActive() { 8315 Node* const promise_hook_or_debug_is_active = 8316 Load(MachineType::Uint8(), 8317 ExternalConstant( 8318 ExternalReference::promise_hook_or_debug_is_active_address( 8319 isolate()))); 8320 return Word32NotEqual(promise_hook_or_debug_is_active, Int32Constant(0)); 8321 } 8322 8323 Node* CodeStubAssembler::AllocateFunctionWithMapAndContext(Node* map, 8324 Node* shared_info, 8325 Node* context) { 8326 Node* const code = BitcastTaggedToWord( 8327 LoadObjectField(shared_info, SharedFunctionInfo::kCodeOffset)); 8328 Node* const code_entry = 8329 IntPtrAdd(code, IntPtrConstant(Code::kHeaderSize - kHeapObjectTag)); 8330 8331 Node* const fun = Allocate(JSFunction::kSize); 8332 StoreMapNoWriteBarrier(fun, map); 8333 StoreObjectFieldRoot(fun, JSObject::kPropertiesOffset, 8334 Heap::kEmptyFixedArrayRootIndex); 8335 StoreObjectFieldRoot(fun, JSObject::kElementsOffset, 8336 Heap::kEmptyFixedArrayRootIndex); 8337 StoreObjectFieldRoot(fun, JSFunction::kFeedbackVectorOffset, 8338 Heap::kUndefinedCellRootIndex); 8339 StoreObjectFieldRoot(fun, JSFunction::kPrototypeOrInitialMapOffset, 8340 Heap::kTheHoleValueRootIndex); 8341 StoreObjectFieldNoWriteBarrier(fun, JSFunction::kSharedFunctionInfoOffset, 8342 shared_info); 8343 StoreObjectFieldNoWriteBarrier(fun, JSFunction::kContextOffset, context); 8344 StoreObjectFieldNoWriteBarrier(fun, JSFunction::kCodeEntryOffset, code_entry, 8345 MachineType::PointerRepresentation()); 8346 StoreObjectFieldRoot(fun, JSFunction::kNextFunctionLinkOffset, 8347 Heap::kUndefinedValueRootIndex); 8348 8349 return fun; 8350 } 8351 8352 Node* CodeStubAssembler::AllocatePromiseReactionJobInfo( 8353 Node* value, Node* tasks, Node* deferred_promise, Node* deferred_on_resolve, 8354 Node* deferred_on_reject, Node* context) { 8355 Node* const result = Allocate(PromiseReactionJobInfo::kSize); 8356 StoreMapNoWriteBarrier(result, Heap::kPromiseReactionJobInfoMapRootIndex); 8357 StoreObjectFieldNoWriteBarrier(result, PromiseReactionJobInfo::kValueOffset, 8358 value); 8359 StoreObjectFieldNoWriteBarrier(result, PromiseReactionJobInfo::kTasksOffset, 8360 tasks); 8361 StoreObjectFieldNoWriteBarrier( 8362 result, PromiseReactionJobInfo::kDeferredPromiseOffset, deferred_promise); 8363 StoreObjectFieldNoWriteBarrier( 8364 result, PromiseReactionJobInfo::kDeferredOnResolveOffset, 8365 deferred_on_resolve); 8366 StoreObjectFieldNoWriteBarrier( 8367 result, PromiseReactionJobInfo::kDeferredOnRejectOffset, 8368 deferred_on_reject); 8369 StoreObjectFieldNoWriteBarrier(result, PromiseReactionJobInfo::kContextOffset, 8370 context); 8371 return result; 8372 } 8373 8374 Node* CodeStubAssembler::MarkerIsFrameType(Node* marker_or_function, 8375 StackFrame::Type frame_type) { 8376 return WordEqual( 8377 marker_or_function, 8378 IntPtrConstant(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); 8379 } 8380 8381 Node* CodeStubAssembler::MarkerIsNotFrameType(Node* marker_or_function, 8382 StackFrame::Type frame_type) { 8383 return WordNotEqual( 8384 marker_or_function, 8385 IntPtrConstant(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); 8386 } 8387 8388 void CodeStubAssembler::Print(const char* s) { 8389 #ifdef DEBUG 8390 std::string formatted(s); 8391 formatted += "\n"; 8392 Handle<String> string = isolate()->factory()->NewStringFromAsciiChecked( 8393 formatted.c_str(), TENURED); 8394 CallRuntime(Runtime::kGlobalPrint, NoContextConstant(), HeapConstant(string)); 8395 #endif 8396 } 8397 8398 void CodeStubAssembler::Print(const char* prefix, Node* tagged_value) { 8399 #ifdef DEBUG 8400 if (prefix != nullptr) { 8401 std::string formatted(prefix); 8402 formatted += ": "; 8403 Handle<String> string = isolate()->factory()->NewStringFromAsciiChecked( 8404 formatted.c_str(), TENURED); 8405 CallRuntime(Runtime::kGlobalPrint, NoContextConstant(), 8406 HeapConstant(string)); 8407 } 8408 CallRuntime(Runtime::kDebugPrint, NoContextConstant(), tagged_value); 8409 #endif 8410 } 8411 8412 } // namespace internal 8413 } // namespace v8 8414