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 5 #include "src/builtins/builtins-utils.h" 6 #include "src/builtins/builtins.h" 7 #include "src/code-factory.h" 8 #include "src/code-stub-assembler.h" 9 #include "src/conversions.h" 10 #include "src/counters.h" 11 #include "src/objects-inl.h" 12 13 namespace v8 { 14 namespace internal { 15 16 class NumberBuiltinsAssembler : public CodeStubAssembler { 17 public: 18 explicit NumberBuiltinsAssembler(compiler::CodeAssemblerState* state) 19 : CodeStubAssembler(state) {} 20 21 protected: 22 template <Signedness signed_result = kSigned> 23 void BitwiseOp(std::function<Node*(Node* lhs, Node* rhs)> body) { 24 Node* left = Parameter(0); 25 Node* right = Parameter(1); 26 Node* context = Parameter(2); 27 28 Node* lhs_value = TruncateTaggedToWord32(context, left); 29 Node* rhs_value = TruncateTaggedToWord32(context, right); 30 Node* value = body(lhs_value, rhs_value); 31 Node* result = signed_result == kSigned ? ChangeInt32ToTagged(value) 32 : ChangeUint32ToTagged(value); 33 Return(result); 34 } 35 36 template <Signedness signed_result = kSigned> 37 void BitwiseShiftOp(std::function<Node*(Node* lhs, Node* shift_count)> body) { 38 BitwiseOp<signed_result>([this, body](Node* lhs, Node* rhs) { 39 Node* shift_count = Word32And(rhs, Int32Constant(0x1f)); 40 return body(lhs, shift_count); 41 }); 42 } 43 44 void RelationalComparisonBuiltin(RelationalComparisonMode mode) { 45 Node* lhs = Parameter(0); 46 Node* rhs = Parameter(1); 47 Node* context = Parameter(2); 48 49 Return(RelationalComparison(mode, lhs, rhs, context)); 50 } 51 }; 52 53 // ----------------------------------------------------------------------------- 54 // ES6 section 20.1 Number Objects 55 56 // ES6 section 20.1.2.2 Number.isFinite ( number ) 57 TF_BUILTIN(NumberIsFinite, CodeStubAssembler) { 58 Node* number = Parameter(1); 59 60 Label return_true(this), return_false(this); 61 62 // Check if {number} is a Smi. 63 GotoIf(TaggedIsSmi(number), &return_true); 64 65 // Check if {number} is a HeapNumber. 66 GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false); 67 68 // Check if {number} contains a finite, non-NaN value. 69 Node* number_value = LoadHeapNumberValue(number); 70 BranchIfFloat64IsNaN(Float64Sub(number_value, number_value), &return_false, 71 &return_true); 72 73 Bind(&return_true); 74 Return(BooleanConstant(true)); 75 76 Bind(&return_false); 77 Return(BooleanConstant(false)); 78 } 79 80 // ES6 section 20.1.2.3 Number.isInteger ( number ) 81 TF_BUILTIN(NumberIsInteger, CodeStubAssembler) { 82 Node* number = Parameter(1); 83 84 Label return_true(this), return_false(this); 85 86 // Check if {number} is a Smi. 87 GotoIf(TaggedIsSmi(number), &return_true); 88 89 // Check if {number} is a HeapNumber. 90 GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false); 91 92 // Load the actual value of {number}. 93 Node* number_value = LoadHeapNumberValue(number); 94 95 // Truncate the value of {number} to an integer (or an infinity). 96 Node* integer = Float64Trunc(number_value); 97 98 // Check if {number}s value matches the integer (ruling out the infinities). 99 Branch(Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)), 100 &return_true, &return_false); 101 102 Bind(&return_true); 103 Return(BooleanConstant(true)); 104 105 Bind(&return_false); 106 Return(BooleanConstant(false)); 107 } 108 109 // ES6 section 20.1.2.4 Number.isNaN ( number ) 110 TF_BUILTIN(NumberIsNaN, CodeStubAssembler) { 111 Node* number = Parameter(1); 112 113 Label return_true(this), return_false(this); 114 115 // Check if {number} is a Smi. 116 GotoIf(TaggedIsSmi(number), &return_false); 117 118 // Check if {number} is a HeapNumber. 119 GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false); 120 121 // Check if {number} contains a NaN value. 122 Node* number_value = LoadHeapNumberValue(number); 123 BranchIfFloat64IsNaN(number_value, &return_true, &return_false); 124 125 Bind(&return_true); 126 Return(BooleanConstant(true)); 127 128 Bind(&return_false); 129 Return(BooleanConstant(false)); 130 } 131 132 // ES6 section 20.1.2.5 Number.isSafeInteger ( number ) 133 TF_BUILTIN(NumberIsSafeInteger, CodeStubAssembler) { 134 Node* number = Parameter(1); 135 136 Label return_true(this), return_false(this); 137 138 // Check if {number} is a Smi. 139 GotoIf(TaggedIsSmi(number), &return_true); 140 141 // Check if {number} is a HeapNumber. 142 GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false); 143 144 // Load the actual value of {number}. 145 Node* number_value = LoadHeapNumberValue(number); 146 147 // Truncate the value of {number} to an integer (or an infinity). 148 Node* integer = Float64Trunc(number_value); 149 150 // Check if {number}s value matches the integer (ruling out the infinities). 151 GotoIfNot( 152 Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)), 153 &return_false); 154 155 // Check if the {integer} value is in safe integer range. 156 Branch(Float64LessThanOrEqual(Float64Abs(integer), 157 Float64Constant(kMaxSafeInteger)), 158 &return_true, &return_false); 159 160 Bind(&return_true); 161 Return(BooleanConstant(true)); 162 163 Bind(&return_false); 164 Return(BooleanConstant(false)); 165 } 166 167 // ES6 section 20.1.2.12 Number.parseFloat ( string ) 168 TF_BUILTIN(NumberParseFloat, CodeStubAssembler) { 169 Node* context = Parameter(4); 170 171 // We might need to loop once for ToString conversion. 172 Variable var_input(this, MachineRepresentation::kTagged); 173 Label loop(this, &var_input); 174 var_input.Bind(Parameter(1)); 175 Goto(&loop); 176 Bind(&loop); 177 { 178 // Load the current {input} value. 179 Node* input = var_input.value(); 180 181 // Check if the {input} is a HeapObject or a Smi. 182 Label if_inputissmi(this), if_inputisnotsmi(this); 183 Branch(TaggedIsSmi(input), &if_inputissmi, &if_inputisnotsmi); 184 185 Bind(&if_inputissmi); 186 { 187 // The {input} is already a Number, no need to do anything. 188 Return(input); 189 } 190 191 Bind(&if_inputisnotsmi); 192 { 193 // The {input} is a HeapObject, check if it's already a String. 194 Label if_inputisstring(this), if_inputisnotstring(this); 195 Node* input_map = LoadMap(input); 196 Node* input_instance_type = LoadMapInstanceType(input_map); 197 Branch(IsStringInstanceType(input_instance_type), &if_inputisstring, 198 &if_inputisnotstring); 199 200 Bind(&if_inputisstring); 201 { 202 // The {input} is already a String, check if {input} contains 203 // a cached array index. 204 Label if_inputcached(this), if_inputnotcached(this); 205 Node* input_hash = LoadNameHashField(input); 206 Node* input_bit = Word32And( 207 input_hash, Int32Constant(String::kContainsCachedArrayIndexMask)); 208 Branch(Word32Equal(input_bit, Int32Constant(0)), &if_inputcached, 209 &if_inputnotcached); 210 211 Bind(&if_inputcached); 212 { 213 // Just return the {input}s cached array index. 214 Node* input_array_index = 215 DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash); 216 Return(SmiTag(input_array_index)); 217 } 218 219 Bind(&if_inputnotcached); 220 { 221 // Need to fall back to the runtime to convert {input} to double. 222 Return(CallRuntime(Runtime::kStringParseFloat, context, input)); 223 } 224 } 225 226 Bind(&if_inputisnotstring); 227 { 228 // The {input} is neither a String nor a Smi, check for HeapNumber. 229 Label if_inputisnumber(this), 230 if_inputisnotnumber(this, Label::kDeferred); 231 Branch(IsHeapNumberMap(input_map), &if_inputisnumber, 232 &if_inputisnotnumber); 233 234 Bind(&if_inputisnumber); 235 { 236 // The {input} is already a Number, take care of -0. 237 Label if_inputiszero(this), if_inputisnotzero(this); 238 Node* input_value = LoadHeapNumberValue(input); 239 Branch(Float64Equal(input_value, Float64Constant(0.0)), 240 &if_inputiszero, &if_inputisnotzero); 241 242 Bind(&if_inputiszero); 243 Return(SmiConstant(0)); 244 245 Bind(&if_inputisnotzero); 246 Return(input); 247 } 248 249 Bind(&if_inputisnotnumber); 250 { 251 // Need to convert the {input} to String first. 252 // TODO(bmeurer): This could be more efficient if necessary. 253 Callable callable = CodeFactory::ToString(isolate()); 254 var_input.Bind(CallStub(callable, context, input)); 255 Goto(&loop); 256 } 257 } 258 } 259 } 260 } 261 262 // ES6 section 20.1.2.13 Number.parseInt ( string, radix ) 263 TF_BUILTIN(NumberParseInt, CodeStubAssembler) { 264 Node* input = Parameter(1); 265 Node* radix = Parameter(2); 266 Node* context = Parameter(5); 267 268 // Check if {radix} is treated as 10 (i.e. undefined, 0 or 10). 269 Label if_radix10(this), if_generic(this, Label::kDeferred); 270 GotoIf(WordEqual(radix, UndefinedConstant()), &if_radix10); 271 GotoIf(WordEqual(radix, SmiConstant(Smi::FromInt(10))), &if_radix10); 272 GotoIf(WordEqual(radix, SmiConstant(Smi::FromInt(0))), &if_radix10); 273 Goto(&if_generic); 274 275 Bind(&if_radix10); 276 { 277 // Check if we can avoid the ToString conversion on {input}. 278 Label if_inputissmi(this), if_inputisheapnumber(this), 279 if_inputisstring(this); 280 GotoIf(TaggedIsSmi(input), &if_inputissmi); 281 Node* input_map = LoadMap(input); 282 GotoIf(IsHeapNumberMap(input_map), &if_inputisheapnumber); 283 Node* input_instance_type = LoadMapInstanceType(input_map); 284 Branch(IsStringInstanceType(input_instance_type), &if_inputisstring, 285 &if_generic); 286 287 Bind(&if_inputissmi); 288 { 289 // Just return the {input}. 290 Return(input); 291 } 292 293 Bind(&if_inputisheapnumber); 294 { 295 // Check if the {input} value is in Signed32 range. 296 Label if_inputissigned32(this); 297 Node* input_value = LoadHeapNumberValue(input); 298 Node* input_value32 = TruncateFloat64ToWord32(input_value); 299 GotoIf(Float64Equal(input_value, ChangeInt32ToFloat64(input_value32)), 300 &if_inputissigned32); 301 302 // Check if the absolute {input} value is in the ]0.01,1e9[ range. 303 Node* input_value_abs = Float64Abs(input_value); 304 305 GotoIfNot(Float64LessThan(input_value_abs, Float64Constant(1e9)), 306 &if_generic); 307 Branch(Float64LessThan(Float64Constant(0.01), input_value_abs), 308 &if_inputissigned32, &if_generic); 309 310 // Return the truncated int32 value, and return the tagged result. 311 Bind(&if_inputissigned32); 312 Node* result = ChangeInt32ToTagged(input_value32); 313 Return(result); 314 } 315 316 Bind(&if_inputisstring); 317 { 318 // Check if the String {input} has a cached array index. 319 Node* input_hash = LoadNameHashField(input); 320 Node* input_bit = Word32And( 321 input_hash, Int32Constant(String::kContainsCachedArrayIndexMask)); 322 GotoIf(Word32NotEqual(input_bit, Int32Constant(0)), &if_generic); 323 324 // Return the cached array index as result. 325 Node* input_index = 326 DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash); 327 Node* result = SmiTag(input_index); 328 Return(result); 329 } 330 } 331 332 Bind(&if_generic); 333 { 334 Node* result = CallRuntime(Runtime::kStringParseInt, context, input, radix); 335 Return(result); 336 } 337 } 338 339 // ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits ) 340 BUILTIN(NumberPrototypeToExponential) { 341 HandleScope scope(isolate); 342 Handle<Object> value = args.at(0); 343 Handle<Object> fraction_digits = args.atOrUndefined(isolate, 1); 344 345 // Unwrap the receiver {value}. 346 if (value->IsJSValue()) { 347 value = handle(Handle<JSValue>::cast(value)->value(), isolate); 348 } 349 if (!value->IsNumber()) { 350 THROW_NEW_ERROR_RETURN_FAILURE( 351 isolate, NewTypeError(MessageTemplate::kNotGeneric, 352 isolate->factory()->NewStringFromAsciiChecked( 353 "Number.prototype.toExponential"))); 354 } 355 double const value_number = value->Number(); 356 357 // Convert the {fraction_digits} to an integer first. 358 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 359 isolate, fraction_digits, Object::ToInteger(isolate, fraction_digits)); 360 double const fraction_digits_number = fraction_digits->Number(); 361 362 if (std::isnan(value_number)) return isolate->heap()->nan_string(); 363 if (std::isinf(value_number)) { 364 return (value_number < 0.0) ? isolate->heap()->minus_infinity_string() 365 : isolate->heap()->infinity_string(); 366 } 367 if (fraction_digits_number < 0.0 || fraction_digits_number > 20.0) { 368 THROW_NEW_ERROR_RETURN_FAILURE( 369 isolate, NewRangeError(MessageTemplate::kNumberFormatRange, 370 isolate->factory()->NewStringFromAsciiChecked( 371 "toExponential()"))); 372 } 373 int const f = args.atOrUndefined(isolate, 1)->IsUndefined(isolate) 374 ? -1 375 : static_cast<int>(fraction_digits_number); 376 char* const str = DoubleToExponentialCString(value_number, f); 377 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str); 378 DeleteArray(str); 379 return *result; 380 } 381 382 // ES6 section 20.1.3.3 Number.prototype.toFixed ( fractionDigits ) 383 BUILTIN(NumberPrototypeToFixed) { 384 HandleScope scope(isolate); 385 Handle<Object> value = args.at(0); 386 Handle<Object> fraction_digits = args.atOrUndefined(isolate, 1); 387 388 // Unwrap the receiver {value}. 389 if (value->IsJSValue()) { 390 value = handle(Handle<JSValue>::cast(value)->value(), isolate); 391 } 392 if (!value->IsNumber()) { 393 THROW_NEW_ERROR_RETURN_FAILURE( 394 isolate, NewTypeError(MessageTemplate::kNotGeneric, 395 isolate->factory()->NewStringFromAsciiChecked( 396 "Number.prototype.toFixed"))); 397 } 398 double const value_number = value->Number(); 399 400 // Convert the {fraction_digits} to an integer first. 401 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 402 isolate, fraction_digits, Object::ToInteger(isolate, fraction_digits)); 403 double const fraction_digits_number = fraction_digits->Number(); 404 405 // Check if the {fraction_digits} are in the supported range. 406 if (fraction_digits_number < 0.0 || fraction_digits_number > 20.0) { 407 THROW_NEW_ERROR_RETURN_FAILURE( 408 isolate, NewRangeError(MessageTemplate::kNumberFormatRange, 409 isolate->factory()->NewStringFromAsciiChecked( 410 "toFixed() digits"))); 411 } 412 413 if (std::isnan(value_number)) return isolate->heap()->nan_string(); 414 if (std::isinf(value_number)) { 415 return (value_number < 0.0) ? isolate->heap()->minus_infinity_string() 416 : isolate->heap()->infinity_string(); 417 } 418 char* const str = DoubleToFixedCString( 419 value_number, static_cast<int>(fraction_digits_number)); 420 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str); 421 DeleteArray(str); 422 return *result; 423 } 424 425 // ES6 section 20.1.3.4 Number.prototype.toLocaleString ( [ r1 [ , r2 ] ] ) 426 BUILTIN(NumberPrototypeToLocaleString) { 427 HandleScope scope(isolate); 428 Handle<Object> value = args.at(0); 429 430 // Unwrap the receiver {value}. 431 if (value->IsJSValue()) { 432 value = handle(Handle<JSValue>::cast(value)->value(), isolate); 433 } 434 if (!value->IsNumber()) { 435 THROW_NEW_ERROR_RETURN_FAILURE( 436 isolate, NewTypeError(MessageTemplate::kNotGeneric, 437 isolate->factory()->NewStringFromAsciiChecked( 438 "Number.prototype.toLocaleString"))); 439 } 440 441 // Turn the {value} into a String. 442 return *isolate->factory()->NumberToString(value); 443 } 444 445 // ES6 section 20.1.3.5 Number.prototype.toPrecision ( precision ) 446 BUILTIN(NumberPrototypeToPrecision) { 447 HandleScope scope(isolate); 448 Handle<Object> value = args.at(0); 449 Handle<Object> precision = args.atOrUndefined(isolate, 1); 450 451 // Unwrap the receiver {value}. 452 if (value->IsJSValue()) { 453 value = handle(Handle<JSValue>::cast(value)->value(), isolate); 454 } 455 if (!value->IsNumber()) { 456 THROW_NEW_ERROR_RETURN_FAILURE( 457 isolate, NewTypeError(MessageTemplate::kNotGeneric, 458 isolate->factory()->NewStringFromAsciiChecked( 459 "Number.prototype.toPrecision"))); 460 } 461 double const value_number = value->Number(); 462 463 // If no {precision} was specified, just return ToString of {value}. 464 if (precision->IsUndefined(isolate)) { 465 return *isolate->factory()->NumberToString(value); 466 } 467 468 // Convert the {precision} to an integer first. 469 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, precision, 470 Object::ToInteger(isolate, precision)); 471 double const precision_number = precision->Number(); 472 473 if (std::isnan(value_number)) return isolate->heap()->nan_string(); 474 if (std::isinf(value_number)) { 475 return (value_number < 0.0) ? isolate->heap()->minus_infinity_string() 476 : isolate->heap()->infinity_string(); 477 } 478 if (precision_number < 1.0 || precision_number > 21.0) { 479 THROW_NEW_ERROR_RETURN_FAILURE( 480 isolate, NewRangeError(MessageTemplate::kToPrecisionFormatRange)); 481 } 482 char* const str = DoubleToPrecisionCString( 483 value_number, static_cast<int>(precision_number)); 484 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str); 485 DeleteArray(str); 486 return *result; 487 } 488 489 // ES6 section 20.1.3.6 Number.prototype.toString ( [ radix ] ) 490 BUILTIN(NumberPrototypeToString) { 491 HandleScope scope(isolate); 492 Handle<Object> value = args.at(0); 493 Handle<Object> radix = args.atOrUndefined(isolate, 1); 494 495 // Unwrap the receiver {value}. 496 if (value->IsJSValue()) { 497 value = handle(Handle<JSValue>::cast(value)->value(), isolate); 498 } 499 if (!value->IsNumber()) { 500 THROW_NEW_ERROR_RETURN_FAILURE( 501 isolate, NewTypeError(MessageTemplate::kNotGeneric, 502 isolate->factory()->NewStringFromAsciiChecked( 503 "Number.prototype.toString"))); 504 } 505 double const value_number = value->Number(); 506 507 // If no {radix} was specified, just return ToString of {value}. 508 if (radix->IsUndefined(isolate)) { 509 return *isolate->factory()->NumberToString(value); 510 } 511 512 // Convert the {radix} to an integer first. 513 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, radix, 514 Object::ToInteger(isolate, radix)); 515 double const radix_number = radix->Number(); 516 517 // If {radix} is 10, just return ToString of {value}. 518 if (radix_number == 10.0) return *isolate->factory()->NumberToString(value); 519 520 // Make sure the {radix} is within the valid range. 521 if (radix_number < 2.0 || radix_number > 36.0) { 522 THROW_NEW_ERROR_RETURN_FAILURE( 523 isolate, NewRangeError(MessageTemplate::kToRadixFormatRange)); 524 } 525 526 // Fast case where the result is a one character string. 527 if ((IsUint32Double(value_number) && value_number < radix_number) || 528 value_number == -0.0) { 529 // Character array used for conversion. 530 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 531 return *isolate->factory()->LookupSingleCharacterStringFromCode( 532 kCharTable[static_cast<uint32_t>(value_number)]); 533 } 534 535 // Slow case. 536 if (std::isnan(value_number)) return isolate->heap()->nan_string(); 537 if (std::isinf(value_number)) { 538 return (value_number < 0.0) ? isolate->heap()->minus_infinity_string() 539 : isolate->heap()->infinity_string(); 540 } 541 char* const str = 542 DoubleToRadixCString(value_number, static_cast<int>(radix_number)); 543 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str); 544 DeleteArray(str); 545 return *result; 546 } 547 548 // ES6 section 20.1.3.7 Number.prototype.valueOf ( ) 549 TF_BUILTIN(NumberPrototypeValueOf, CodeStubAssembler) { 550 Node* receiver = Parameter(0); 551 Node* context = Parameter(3); 552 553 Node* result = ToThisValue(context, receiver, PrimitiveType::kNumber, 554 "Number.prototype.valueOf"); 555 Return(result); 556 } 557 558 TF_BUILTIN(Add, CodeStubAssembler) { 559 Node* left = Parameter(0); 560 Node* right = Parameter(1); 561 Node* context = Parameter(2); 562 563 // Shared entry for floating point addition. 564 Label do_fadd(this); 565 Variable var_fadd_lhs(this, MachineRepresentation::kFloat64), 566 var_fadd_rhs(this, MachineRepresentation::kFloat64); 567 568 // We might need to loop several times due to ToPrimitive, ToString and/or 569 // ToNumber conversions. 570 Variable var_lhs(this, MachineRepresentation::kTagged), 571 var_rhs(this, MachineRepresentation::kTagged), 572 var_result(this, MachineRepresentation::kTagged); 573 Variable* loop_vars[2] = {&var_lhs, &var_rhs}; 574 Label loop(this, 2, loop_vars), end(this), 575 string_add_convert_left(this, Label::kDeferred), 576 string_add_convert_right(this, Label::kDeferred); 577 var_lhs.Bind(left); 578 var_rhs.Bind(right); 579 Goto(&loop); 580 Bind(&loop); 581 { 582 // Load the current {lhs} and {rhs} values. 583 Node* lhs = var_lhs.value(); 584 Node* rhs = var_rhs.value(); 585 586 // Check if the {lhs} is a Smi or a HeapObject. 587 Label if_lhsissmi(this), if_lhsisnotsmi(this); 588 Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); 589 590 Bind(&if_lhsissmi); 591 { 592 // Check if the {rhs} is also a Smi. 593 Label if_rhsissmi(this), if_rhsisnotsmi(this); 594 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 595 596 Bind(&if_rhsissmi); 597 { 598 // Try fast Smi addition first. 599 Node* pair = IntPtrAddWithOverflow(BitcastTaggedToWord(lhs), 600 BitcastTaggedToWord(rhs)); 601 Node* overflow = Projection(1, pair); 602 603 // Check if the Smi additon overflowed. 604 Label if_overflow(this), if_notoverflow(this); 605 Branch(overflow, &if_overflow, &if_notoverflow); 606 607 Bind(&if_overflow); 608 { 609 var_fadd_lhs.Bind(SmiToFloat64(lhs)); 610 var_fadd_rhs.Bind(SmiToFloat64(rhs)); 611 Goto(&do_fadd); 612 } 613 614 Bind(&if_notoverflow); 615 var_result.Bind(BitcastWordToTaggedSigned(Projection(0, pair))); 616 Goto(&end); 617 } 618 619 Bind(&if_rhsisnotsmi); 620 { 621 // Load the map of {rhs}. 622 Node* rhs_map = LoadMap(rhs); 623 624 // Check if the {rhs} is a HeapNumber. 625 Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred); 626 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); 627 628 Bind(&if_rhsisnumber); 629 { 630 var_fadd_lhs.Bind(SmiToFloat64(lhs)); 631 var_fadd_rhs.Bind(LoadHeapNumberValue(rhs)); 632 Goto(&do_fadd); 633 } 634 635 Bind(&if_rhsisnotnumber); 636 { 637 // Load the instance type of {rhs}. 638 Node* rhs_instance_type = LoadMapInstanceType(rhs_map); 639 640 // Check if the {rhs} is a String. 641 Label if_rhsisstring(this, Label::kDeferred), 642 if_rhsisnotstring(this, Label::kDeferred); 643 Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, 644 &if_rhsisnotstring); 645 646 Bind(&if_rhsisstring); 647 { 648 var_lhs.Bind(lhs); 649 var_rhs.Bind(rhs); 650 Goto(&string_add_convert_left); 651 } 652 653 Bind(&if_rhsisnotstring); 654 { 655 // Check if {rhs} is a JSReceiver. 656 Label if_rhsisreceiver(this, Label::kDeferred), 657 if_rhsisnotreceiver(this, Label::kDeferred); 658 Branch(IsJSReceiverInstanceType(rhs_instance_type), 659 &if_rhsisreceiver, &if_rhsisnotreceiver); 660 661 Bind(&if_rhsisreceiver); 662 { 663 // Convert {rhs} to a primitive first passing no hint. 664 Callable callable = 665 CodeFactory::NonPrimitiveToPrimitive(isolate()); 666 var_rhs.Bind(CallStub(callable, context, rhs)); 667 Goto(&loop); 668 } 669 670 Bind(&if_rhsisnotreceiver); 671 { 672 // Convert {rhs} to a Number first. 673 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 674 var_rhs.Bind(CallStub(callable, context, rhs)); 675 Goto(&loop); 676 } 677 } 678 } 679 } 680 } 681 682 Bind(&if_lhsisnotsmi); 683 { 684 // Load the map and instance type of {lhs}. 685 Node* lhs_instance_type = LoadInstanceType(lhs); 686 687 // Check if {lhs} is a String. 688 Label if_lhsisstring(this), if_lhsisnotstring(this); 689 Branch(IsStringInstanceType(lhs_instance_type), &if_lhsisstring, 690 &if_lhsisnotstring); 691 692 Bind(&if_lhsisstring); 693 { 694 var_lhs.Bind(lhs); 695 var_rhs.Bind(rhs); 696 Goto(&string_add_convert_right); 697 } 698 699 Bind(&if_lhsisnotstring); 700 { 701 // Check if {rhs} is a Smi. 702 Label if_rhsissmi(this), if_rhsisnotsmi(this); 703 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 704 705 Bind(&if_rhsissmi); 706 { 707 // Check if {lhs} is a Number. 708 Label if_lhsisnumber(this), if_lhsisnotnumber(this, Label::kDeferred); 709 Branch( 710 Word32Equal(lhs_instance_type, Int32Constant(HEAP_NUMBER_TYPE)), 711 &if_lhsisnumber, &if_lhsisnotnumber); 712 713 Bind(&if_lhsisnumber); 714 { 715 // The {lhs} is a HeapNumber, the {rhs} is a Smi, just add them. 716 var_fadd_lhs.Bind(LoadHeapNumberValue(lhs)); 717 var_fadd_rhs.Bind(SmiToFloat64(rhs)); 718 Goto(&do_fadd); 719 } 720 721 Bind(&if_lhsisnotnumber); 722 { 723 // The {lhs} is neither a Number nor a String, and the {rhs} is a 724 // Smi. 725 Label if_lhsisreceiver(this, Label::kDeferred), 726 if_lhsisnotreceiver(this, Label::kDeferred); 727 Branch(IsJSReceiverInstanceType(lhs_instance_type), 728 &if_lhsisreceiver, &if_lhsisnotreceiver); 729 730 Bind(&if_lhsisreceiver); 731 { 732 // Convert {lhs} to a primitive first passing no hint. 733 Callable callable = 734 CodeFactory::NonPrimitiveToPrimitive(isolate()); 735 var_lhs.Bind(CallStub(callable, context, lhs)); 736 Goto(&loop); 737 } 738 739 Bind(&if_lhsisnotreceiver); 740 { 741 // Convert {lhs} to a Number first. 742 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 743 var_lhs.Bind(CallStub(callable, context, lhs)); 744 Goto(&loop); 745 } 746 } 747 } 748 749 Bind(&if_rhsisnotsmi); 750 { 751 // Load the instance type of {rhs}. 752 Node* rhs_instance_type = LoadInstanceType(rhs); 753 754 // Check if {rhs} is a String. 755 Label if_rhsisstring(this), if_rhsisnotstring(this); 756 Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, 757 &if_rhsisnotstring); 758 759 Bind(&if_rhsisstring); 760 { 761 var_lhs.Bind(lhs); 762 var_rhs.Bind(rhs); 763 Goto(&string_add_convert_left); 764 } 765 766 Bind(&if_rhsisnotstring); 767 { 768 // Check if {lhs} is a HeapNumber. 769 Label if_lhsisnumber(this), if_lhsisnotnumber(this); 770 Branch( 771 Word32Equal(lhs_instance_type, Int32Constant(HEAP_NUMBER_TYPE)), 772 &if_lhsisnumber, &if_lhsisnotnumber); 773 774 Bind(&if_lhsisnumber); 775 { 776 // Check if {rhs} is also a HeapNumber. 777 Label if_rhsisnumber(this), 778 if_rhsisnotnumber(this, Label::kDeferred); 779 Branch(Word32Equal(rhs_instance_type, 780 Int32Constant(HEAP_NUMBER_TYPE)), 781 &if_rhsisnumber, &if_rhsisnotnumber); 782 783 Bind(&if_rhsisnumber); 784 { 785 // Perform a floating point addition. 786 var_fadd_lhs.Bind(LoadHeapNumberValue(lhs)); 787 var_fadd_rhs.Bind(LoadHeapNumberValue(rhs)); 788 Goto(&do_fadd); 789 } 790 791 Bind(&if_rhsisnotnumber); 792 { 793 // Check if {rhs} is a JSReceiver. 794 Label if_rhsisreceiver(this, Label::kDeferred), 795 if_rhsisnotreceiver(this, Label::kDeferred); 796 Branch(IsJSReceiverInstanceType(rhs_instance_type), 797 &if_rhsisreceiver, &if_rhsisnotreceiver); 798 799 Bind(&if_rhsisreceiver); 800 { 801 // Convert {rhs} to a primitive first passing no hint. 802 Callable callable = 803 CodeFactory::NonPrimitiveToPrimitive(isolate()); 804 var_rhs.Bind(CallStub(callable, context, rhs)); 805 Goto(&loop); 806 } 807 808 Bind(&if_rhsisnotreceiver); 809 { 810 // Convert {rhs} to a Number first. 811 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 812 var_rhs.Bind(CallStub(callable, context, rhs)); 813 Goto(&loop); 814 } 815 } 816 } 817 818 Bind(&if_lhsisnotnumber); 819 { 820 // Check if {lhs} is a JSReceiver. 821 Label if_lhsisreceiver(this, Label::kDeferred), 822 if_lhsisnotreceiver(this); 823 Branch(IsJSReceiverInstanceType(lhs_instance_type), 824 &if_lhsisreceiver, &if_lhsisnotreceiver); 825 826 Bind(&if_lhsisreceiver); 827 { 828 // Convert {lhs} to a primitive first passing no hint. 829 Callable callable = 830 CodeFactory::NonPrimitiveToPrimitive(isolate()); 831 var_lhs.Bind(CallStub(callable, context, lhs)); 832 Goto(&loop); 833 } 834 835 Bind(&if_lhsisnotreceiver); 836 { 837 // Check if {rhs} is a JSReceiver. 838 Label if_rhsisreceiver(this, Label::kDeferred), 839 if_rhsisnotreceiver(this, Label::kDeferred); 840 Branch(IsJSReceiverInstanceType(rhs_instance_type), 841 &if_rhsisreceiver, &if_rhsisnotreceiver); 842 843 Bind(&if_rhsisreceiver); 844 { 845 // Convert {rhs} to a primitive first passing no hint. 846 Callable callable = 847 CodeFactory::NonPrimitiveToPrimitive(isolate()); 848 var_rhs.Bind(CallStub(callable, context, rhs)); 849 Goto(&loop); 850 } 851 852 Bind(&if_rhsisnotreceiver); 853 { 854 // Convert {lhs} to a Number first. 855 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 856 var_lhs.Bind(CallStub(callable, context, lhs)); 857 Goto(&loop); 858 } 859 } 860 } 861 } 862 } 863 } 864 } 865 } 866 Bind(&string_add_convert_left); 867 { 868 // Convert {lhs}, which is a Smi, to a String and concatenate the 869 // resulting string with the String {rhs}. 870 Callable callable = 871 CodeFactory::StringAdd(isolate(), STRING_ADD_CONVERT_LEFT, NOT_TENURED); 872 var_result.Bind( 873 CallStub(callable, context, var_lhs.value(), var_rhs.value())); 874 Goto(&end); 875 } 876 877 Bind(&string_add_convert_right); 878 { 879 // Convert {lhs}, which is a Smi, to a String and concatenate the 880 // resulting string with the String {rhs}. 881 Callable callable = CodeFactory::StringAdd( 882 isolate(), STRING_ADD_CONVERT_RIGHT, NOT_TENURED); 883 var_result.Bind( 884 CallStub(callable, context, var_lhs.value(), var_rhs.value())); 885 Goto(&end); 886 } 887 888 Bind(&do_fadd); 889 { 890 Node* lhs_value = var_fadd_lhs.value(); 891 Node* rhs_value = var_fadd_rhs.value(); 892 Node* value = Float64Add(lhs_value, rhs_value); 893 Node* result = AllocateHeapNumberWithValue(value); 894 var_result.Bind(result); 895 Goto(&end); 896 } 897 Bind(&end); 898 Return(var_result.value()); 899 } 900 901 TF_BUILTIN(Subtract, CodeStubAssembler) { 902 Node* left = Parameter(0); 903 Node* right = Parameter(1); 904 Node* context = Parameter(2); 905 906 // Shared entry for floating point subtraction. 907 Label do_fsub(this), end(this); 908 Variable var_fsub_lhs(this, MachineRepresentation::kFloat64), 909 var_fsub_rhs(this, MachineRepresentation::kFloat64); 910 911 // We might need to loop several times due to ToPrimitive and/or ToNumber 912 // conversions. 913 Variable var_lhs(this, MachineRepresentation::kTagged), 914 var_rhs(this, MachineRepresentation::kTagged), 915 var_result(this, MachineRepresentation::kTagged); 916 Variable* loop_vars[2] = {&var_lhs, &var_rhs}; 917 Label loop(this, 2, loop_vars); 918 var_lhs.Bind(left); 919 var_rhs.Bind(right); 920 Goto(&loop); 921 Bind(&loop); 922 { 923 // Load the current {lhs} and {rhs} values. 924 Node* lhs = var_lhs.value(); 925 Node* rhs = var_rhs.value(); 926 927 // Check if the {lhs} is a Smi or a HeapObject. 928 Label if_lhsissmi(this), if_lhsisnotsmi(this); 929 Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); 930 931 Bind(&if_lhsissmi); 932 { 933 // Check if the {rhs} is also a Smi. 934 Label if_rhsissmi(this), if_rhsisnotsmi(this); 935 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 936 937 Bind(&if_rhsissmi); 938 { 939 // Try a fast Smi subtraction first. 940 Node* pair = IntPtrSubWithOverflow(BitcastTaggedToWord(lhs), 941 BitcastTaggedToWord(rhs)); 942 Node* overflow = Projection(1, pair); 943 944 // Check if the Smi subtraction overflowed. 945 Label if_overflow(this), if_notoverflow(this); 946 Branch(overflow, &if_overflow, &if_notoverflow); 947 948 Bind(&if_overflow); 949 { 950 // The result doesn't fit into Smi range. 951 var_fsub_lhs.Bind(SmiToFloat64(lhs)); 952 var_fsub_rhs.Bind(SmiToFloat64(rhs)); 953 Goto(&do_fsub); 954 } 955 956 Bind(&if_notoverflow); 957 var_result.Bind(BitcastWordToTaggedSigned(Projection(0, pair))); 958 Goto(&end); 959 } 960 961 Bind(&if_rhsisnotsmi); 962 { 963 // Load the map of the {rhs}. 964 Node* rhs_map = LoadMap(rhs); 965 966 // Check if {rhs} is a HeapNumber. 967 Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred); 968 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); 969 970 Bind(&if_rhsisnumber); 971 { 972 // Perform a floating point subtraction. 973 var_fsub_lhs.Bind(SmiToFloat64(lhs)); 974 var_fsub_rhs.Bind(LoadHeapNumberValue(rhs)); 975 Goto(&do_fsub); 976 } 977 978 Bind(&if_rhsisnotnumber); 979 { 980 // Convert the {rhs} to a Number first. 981 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 982 var_rhs.Bind(CallStub(callable, context, rhs)); 983 Goto(&loop); 984 } 985 } 986 } 987 988 Bind(&if_lhsisnotsmi); 989 { 990 // Load the map of the {lhs}. 991 Node* lhs_map = LoadMap(lhs); 992 993 // Check if the {lhs} is a HeapNumber. 994 Label if_lhsisnumber(this), if_lhsisnotnumber(this, Label::kDeferred); 995 Branch(IsHeapNumberMap(lhs_map), &if_lhsisnumber, &if_lhsisnotnumber); 996 997 Bind(&if_lhsisnumber); 998 { 999 // Check if the {rhs} is a Smi. 1000 Label if_rhsissmi(this), if_rhsisnotsmi(this); 1001 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); 1002 1003 Bind(&if_rhsissmi); 1004 { 1005 // Perform a floating point subtraction. 1006 var_fsub_lhs.Bind(LoadHeapNumberValue(lhs)); 1007 var_fsub_rhs.Bind(SmiToFloat64(rhs)); 1008 Goto(&do_fsub); 1009 } 1010 1011 Bind(&if_rhsisnotsmi); 1012 { 1013 // Load the map of the {rhs}. 1014 Node* rhs_map = LoadMap(rhs); 1015 1016 // Check if the {rhs} is a HeapNumber. 1017 Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred); 1018 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); 1019 1020 Bind(&if_rhsisnumber); 1021 { 1022 // Perform a floating point subtraction. 1023 var_fsub_lhs.Bind(LoadHeapNumberValue(lhs)); 1024 var_fsub_rhs.Bind(LoadHeapNumberValue(rhs)); 1025 Goto(&do_fsub); 1026 } 1027 1028 Bind(&if_rhsisnotnumber); 1029 { 1030 // Convert the {rhs} to a Number first. 1031 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 1032 var_rhs.Bind(CallStub(callable, context, rhs)); 1033 Goto(&loop); 1034 } 1035 } 1036 } 1037 1038 Bind(&if_lhsisnotnumber); 1039 { 1040 // Convert the {lhs} to a Number first. 1041 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 1042 var_lhs.Bind(CallStub(callable, context, lhs)); 1043 Goto(&loop); 1044 } 1045 } 1046 } 1047 1048 Bind(&do_fsub); 1049 { 1050 Node* lhs_value = var_fsub_lhs.value(); 1051 Node* rhs_value = var_fsub_rhs.value(); 1052 Node* value = Float64Sub(lhs_value, rhs_value); 1053 var_result.Bind(AllocateHeapNumberWithValue(value)); 1054 Goto(&end); 1055 } 1056 Bind(&end); 1057 Return(var_result.value()); 1058 } 1059 1060 TF_BUILTIN(Multiply, CodeStubAssembler) { 1061 Node* left = Parameter(0); 1062 Node* right = Parameter(1); 1063 Node* context = Parameter(2); 1064 1065 // Shared entry point for floating point multiplication. 1066 Label do_fmul(this), return_result(this); 1067 Variable var_lhs_float64(this, MachineRepresentation::kFloat64), 1068 var_rhs_float64(this, MachineRepresentation::kFloat64); 1069 1070 // We might need to loop one or two times due to ToNumber conversions. 1071 Variable var_lhs(this, MachineRepresentation::kTagged), 1072 var_rhs(this, MachineRepresentation::kTagged), 1073 var_result(this, MachineRepresentation::kTagged); 1074 Variable* loop_variables[] = {&var_lhs, &var_rhs}; 1075 Label loop(this, 2, loop_variables); 1076 var_lhs.Bind(left); 1077 var_rhs.Bind(right); 1078 Goto(&loop); 1079 Bind(&loop); 1080 { 1081 Node* lhs = var_lhs.value(); 1082 Node* rhs = var_rhs.value(); 1083 1084 Label lhs_is_smi(this), lhs_is_not_smi(this); 1085 Branch(TaggedIsSmi(lhs), &lhs_is_smi, &lhs_is_not_smi); 1086 1087 Bind(&lhs_is_smi); 1088 { 1089 Label rhs_is_smi(this), rhs_is_not_smi(this); 1090 Branch(TaggedIsSmi(rhs), &rhs_is_smi, &rhs_is_not_smi); 1091 1092 Bind(&rhs_is_smi); 1093 { 1094 // Both {lhs} and {rhs} are Smis. The result is not necessarily a smi, 1095 // in case of overflow. 1096 var_result.Bind(SmiMul(lhs, rhs)); 1097 Goto(&return_result); 1098 } 1099 1100 Bind(&rhs_is_not_smi); 1101 { 1102 Node* rhs_map = LoadMap(rhs); 1103 1104 // Check if {rhs} is a HeapNumber. 1105 Label rhs_is_number(this), rhs_is_not_number(this, Label::kDeferred); 1106 Branch(IsHeapNumberMap(rhs_map), &rhs_is_number, &rhs_is_not_number); 1107 1108 Bind(&rhs_is_number); 1109 { 1110 // Convert {lhs} to a double and multiply it with the value of {rhs}. 1111 var_lhs_float64.Bind(SmiToFloat64(lhs)); 1112 var_rhs_float64.Bind(LoadHeapNumberValue(rhs)); 1113 Goto(&do_fmul); 1114 } 1115 1116 Bind(&rhs_is_not_number); 1117 { 1118 // Multiplication is commutative, swap {lhs} with {rhs} and loop. 1119 var_lhs.Bind(rhs); 1120 var_rhs.Bind(lhs); 1121 Goto(&loop); 1122 } 1123 } 1124 } 1125 1126 Bind(&lhs_is_not_smi); 1127 { 1128 Node* lhs_map = LoadMap(lhs); 1129 1130 // Check if {lhs} is a HeapNumber. 1131 Label lhs_is_number(this), lhs_is_not_number(this, Label::kDeferred); 1132 Branch(IsHeapNumberMap(lhs_map), &lhs_is_number, &lhs_is_not_number); 1133 1134 Bind(&lhs_is_number); 1135 { 1136 // Check if {rhs} is a Smi. 1137 Label rhs_is_smi(this), rhs_is_not_smi(this); 1138 Branch(TaggedIsSmi(rhs), &rhs_is_smi, &rhs_is_not_smi); 1139 1140 Bind(&rhs_is_smi); 1141 { 1142 // Convert {rhs} to a double and multiply it with the value of {lhs}. 1143 var_lhs_float64.Bind(LoadHeapNumberValue(lhs)); 1144 var_rhs_float64.Bind(SmiToFloat64(rhs)); 1145 Goto(&do_fmul); 1146 } 1147 1148 Bind(&rhs_is_not_smi); 1149 { 1150 Node* rhs_map = LoadMap(rhs); 1151 1152 // Check if {rhs} is a HeapNumber. 1153 Label rhs_is_number(this), rhs_is_not_number(this, Label::kDeferred); 1154 Branch(IsHeapNumberMap(rhs_map), &rhs_is_number, &rhs_is_not_number); 1155 1156 Bind(&rhs_is_number); 1157 { 1158 // Both {lhs} and {rhs} are HeapNumbers. Load their values and 1159 // multiply them. 1160 var_lhs_float64.Bind(LoadHeapNumberValue(lhs)); 1161 var_rhs_float64.Bind(LoadHeapNumberValue(rhs)); 1162 Goto(&do_fmul); 1163 } 1164 1165 Bind(&rhs_is_not_number); 1166 { 1167 // Multiplication is commutative, swap {lhs} with {rhs} and loop. 1168 var_lhs.Bind(rhs); 1169 var_rhs.Bind(lhs); 1170 Goto(&loop); 1171 } 1172 } 1173 } 1174 1175 Bind(&lhs_is_not_number); 1176 { 1177 // Convert {lhs} to a Number and loop. 1178 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 1179 var_lhs.Bind(CallStub(callable, context, lhs)); 1180 Goto(&loop); 1181 } 1182 } 1183 } 1184 1185 Bind(&do_fmul); 1186 { 1187 Node* value = Float64Mul(var_lhs_float64.value(), var_rhs_float64.value()); 1188 Node* result = AllocateHeapNumberWithValue(value); 1189 var_result.Bind(result); 1190 Goto(&return_result); 1191 } 1192 1193 Bind(&return_result); 1194 Return(var_result.value()); 1195 } 1196 1197 TF_BUILTIN(Divide, CodeStubAssembler) { 1198 Node* left = Parameter(0); 1199 Node* right = Parameter(1); 1200 Node* context = Parameter(2); 1201 1202 // Shared entry point for floating point division. 1203 Label do_fdiv(this), end(this); 1204 Variable var_dividend_float64(this, MachineRepresentation::kFloat64), 1205 var_divisor_float64(this, MachineRepresentation::kFloat64); 1206 1207 // We might need to loop one or two times due to ToNumber conversions. 1208 Variable var_dividend(this, MachineRepresentation::kTagged), 1209 var_divisor(this, MachineRepresentation::kTagged), 1210 var_result(this, MachineRepresentation::kTagged); 1211 Variable* loop_variables[] = {&var_dividend, &var_divisor}; 1212 Label loop(this, 2, loop_variables); 1213 var_dividend.Bind(left); 1214 var_divisor.Bind(right); 1215 Goto(&loop); 1216 Bind(&loop); 1217 { 1218 Node* dividend = var_dividend.value(); 1219 Node* divisor = var_divisor.value(); 1220 1221 Label dividend_is_smi(this), dividend_is_not_smi(this); 1222 Branch(TaggedIsSmi(dividend), ÷nd_is_smi, ÷nd_is_not_smi); 1223 1224 Bind(÷nd_is_smi); 1225 { 1226 Label divisor_is_smi(this), divisor_is_not_smi(this); 1227 Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi); 1228 1229 Bind(&divisor_is_smi); 1230 { 1231 Label bailout(this); 1232 1233 // Do floating point division if {divisor} is zero. 1234 GotoIf(SmiEqual(divisor, SmiConstant(0)), &bailout); 1235 1236 // Do floating point division {dividend} is zero and {divisor} is 1237 // negative. 1238 Label dividend_is_zero(this), dividend_is_not_zero(this); 1239 Branch(SmiEqual(dividend, SmiConstant(0)), ÷nd_is_zero, 1240 ÷nd_is_not_zero); 1241 1242 Bind(÷nd_is_zero); 1243 { 1244 GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout); 1245 Goto(÷nd_is_not_zero); 1246 } 1247 Bind(÷nd_is_not_zero); 1248 1249 Node* untagged_divisor = SmiToWord32(divisor); 1250 Node* untagged_dividend = SmiToWord32(dividend); 1251 1252 // Do floating point division if {dividend} is kMinInt (or kMinInt - 1 1253 // if the Smi size is 31) and {divisor} is -1. 1254 Label divisor_is_minus_one(this), divisor_is_not_minus_one(this); 1255 Branch(Word32Equal(untagged_divisor, Int32Constant(-1)), 1256 &divisor_is_minus_one, &divisor_is_not_minus_one); 1257 1258 Bind(&divisor_is_minus_one); 1259 { 1260 GotoIf( 1261 Word32Equal(untagged_dividend, 1262 Int32Constant(kSmiValueSize == 32 ? kMinInt 1263 : (kMinInt >> 1))), 1264 &bailout); 1265 Goto(&divisor_is_not_minus_one); 1266 } 1267 Bind(&divisor_is_not_minus_one); 1268 1269 // TODO(epertoso): consider adding a machine instruction that returns 1270 // both the result and the remainder. 1271 Node* untagged_result = Int32Div(untagged_dividend, untagged_divisor); 1272 Node* truncated = Int32Mul(untagged_result, untagged_divisor); 1273 // Do floating point division if the remainder is not 0. 1274 GotoIf(Word32NotEqual(untagged_dividend, truncated), &bailout); 1275 var_result.Bind(SmiFromWord32(untagged_result)); 1276 Goto(&end); 1277 1278 // Bailout: convert {dividend} and {divisor} to double and do double 1279 // division. 1280 Bind(&bailout); 1281 { 1282 var_dividend_float64.Bind(SmiToFloat64(dividend)); 1283 var_divisor_float64.Bind(SmiToFloat64(divisor)); 1284 Goto(&do_fdiv); 1285 } 1286 } 1287 1288 Bind(&divisor_is_not_smi); 1289 { 1290 Node* divisor_map = LoadMap(divisor); 1291 1292 // Check if {divisor} is a HeapNumber. 1293 Label divisor_is_number(this), 1294 divisor_is_not_number(this, Label::kDeferred); 1295 Branch(IsHeapNumberMap(divisor_map), &divisor_is_number, 1296 &divisor_is_not_number); 1297 1298 Bind(&divisor_is_number); 1299 { 1300 // Convert {dividend} to a double and divide it with the value of 1301 // {divisor}. 1302 var_dividend_float64.Bind(SmiToFloat64(dividend)); 1303 var_divisor_float64.Bind(LoadHeapNumberValue(divisor)); 1304 Goto(&do_fdiv); 1305 } 1306 1307 Bind(&divisor_is_not_number); 1308 { 1309 // Convert {divisor} to a number and loop. 1310 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 1311 var_divisor.Bind(CallStub(callable, context, divisor)); 1312 Goto(&loop); 1313 } 1314 } 1315 } 1316 1317 Bind(÷nd_is_not_smi); 1318 { 1319 Node* dividend_map = LoadMap(dividend); 1320 1321 // Check if {dividend} is a HeapNumber. 1322 Label dividend_is_number(this), 1323 dividend_is_not_number(this, Label::kDeferred); 1324 Branch(IsHeapNumberMap(dividend_map), ÷nd_is_number, 1325 ÷nd_is_not_number); 1326 1327 Bind(÷nd_is_number); 1328 { 1329 // Check if {divisor} is a Smi. 1330 Label divisor_is_smi(this), divisor_is_not_smi(this); 1331 Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi); 1332 1333 Bind(&divisor_is_smi); 1334 { 1335 // Convert {divisor} to a double and use it for a floating point 1336 // division. 1337 var_dividend_float64.Bind(LoadHeapNumberValue(dividend)); 1338 var_divisor_float64.Bind(SmiToFloat64(divisor)); 1339 Goto(&do_fdiv); 1340 } 1341 1342 Bind(&divisor_is_not_smi); 1343 { 1344 Node* divisor_map = LoadMap(divisor); 1345 1346 // Check if {divisor} is a HeapNumber. 1347 Label divisor_is_number(this), 1348 divisor_is_not_number(this, Label::kDeferred); 1349 Branch(IsHeapNumberMap(divisor_map), &divisor_is_number, 1350 &divisor_is_not_number); 1351 1352 Bind(&divisor_is_number); 1353 { 1354 // Both {dividend} and {divisor} are HeapNumbers. Load their values 1355 // and divide them. 1356 var_dividend_float64.Bind(LoadHeapNumberValue(dividend)); 1357 var_divisor_float64.Bind(LoadHeapNumberValue(divisor)); 1358 Goto(&do_fdiv); 1359 } 1360 1361 Bind(&divisor_is_not_number); 1362 { 1363 // Convert {divisor} to a number and loop. 1364 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 1365 var_divisor.Bind(CallStub(callable, context, divisor)); 1366 Goto(&loop); 1367 } 1368 } 1369 } 1370 1371 Bind(÷nd_is_not_number); 1372 { 1373 // Convert {dividend} to a Number and loop. 1374 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 1375 var_dividend.Bind(CallStub(callable, context, dividend)); 1376 Goto(&loop); 1377 } 1378 } 1379 } 1380 1381 Bind(&do_fdiv); 1382 { 1383 Node* value = 1384 Float64Div(var_dividend_float64.value(), var_divisor_float64.value()); 1385 var_result.Bind(AllocateHeapNumberWithValue(value)); 1386 Goto(&end); 1387 } 1388 Bind(&end); 1389 Return(var_result.value()); 1390 } 1391 1392 TF_BUILTIN(Modulus, CodeStubAssembler) { 1393 Node* left = Parameter(0); 1394 Node* right = Parameter(1); 1395 Node* context = Parameter(2); 1396 1397 Variable var_result(this, MachineRepresentation::kTagged); 1398 Label return_result(this, &var_result); 1399 1400 // Shared entry point for floating point modulus. 1401 Label do_fmod(this); 1402 Variable var_dividend_float64(this, MachineRepresentation::kFloat64), 1403 var_divisor_float64(this, MachineRepresentation::kFloat64); 1404 1405 // We might need to loop one or two times due to ToNumber conversions. 1406 Variable var_dividend(this, MachineRepresentation::kTagged), 1407 var_divisor(this, MachineRepresentation::kTagged); 1408 Variable* loop_variables[] = {&var_dividend, &var_divisor}; 1409 Label loop(this, 2, loop_variables); 1410 var_dividend.Bind(left); 1411 var_divisor.Bind(right); 1412 Goto(&loop); 1413 Bind(&loop); 1414 { 1415 Node* dividend = var_dividend.value(); 1416 Node* divisor = var_divisor.value(); 1417 1418 Label dividend_is_smi(this), dividend_is_not_smi(this); 1419 Branch(TaggedIsSmi(dividend), ÷nd_is_smi, ÷nd_is_not_smi); 1420 1421 Bind(÷nd_is_smi); 1422 { 1423 Label dividend_is_not_zero(this); 1424 Label divisor_is_smi(this), divisor_is_not_smi(this); 1425 Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi); 1426 1427 Bind(&divisor_is_smi); 1428 { 1429 // Compute the modulus of two Smis. 1430 var_result.Bind(SmiMod(dividend, divisor)); 1431 Goto(&return_result); 1432 } 1433 1434 Bind(&divisor_is_not_smi); 1435 { 1436 Node* divisor_map = LoadMap(divisor); 1437 1438 // Check if {divisor} is a HeapNumber. 1439 Label divisor_is_number(this), 1440 divisor_is_not_number(this, Label::kDeferred); 1441 Branch(IsHeapNumberMap(divisor_map), &divisor_is_number, 1442 &divisor_is_not_number); 1443 1444 Bind(&divisor_is_number); 1445 { 1446 // Convert {dividend} to a double and compute its modulus with the 1447 // value of {dividend}. 1448 var_dividend_float64.Bind(SmiToFloat64(dividend)); 1449 var_divisor_float64.Bind(LoadHeapNumberValue(divisor)); 1450 Goto(&do_fmod); 1451 } 1452 1453 Bind(&divisor_is_not_number); 1454 { 1455 // Convert {divisor} to a number and loop. 1456 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 1457 var_divisor.Bind(CallStub(callable, context, divisor)); 1458 Goto(&loop); 1459 } 1460 } 1461 } 1462 1463 Bind(÷nd_is_not_smi); 1464 { 1465 Node* dividend_map = LoadMap(dividend); 1466 1467 // Check if {dividend} is a HeapNumber. 1468 Label dividend_is_number(this), 1469 dividend_is_not_number(this, Label::kDeferred); 1470 Branch(IsHeapNumberMap(dividend_map), ÷nd_is_number, 1471 ÷nd_is_not_number); 1472 1473 Bind(÷nd_is_number); 1474 { 1475 // Check if {divisor} is a Smi. 1476 Label divisor_is_smi(this), divisor_is_not_smi(this); 1477 Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi); 1478 1479 Bind(&divisor_is_smi); 1480 { 1481 // Convert {divisor} to a double and compute {dividend}'s modulus with 1482 // it. 1483 var_dividend_float64.Bind(LoadHeapNumberValue(dividend)); 1484 var_divisor_float64.Bind(SmiToFloat64(divisor)); 1485 Goto(&do_fmod); 1486 } 1487 1488 Bind(&divisor_is_not_smi); 1489 { 1490 Node* divisor_map = LoadMap(divisor); 1491 1492 // Check if {divisor} is a HeapNumber. 1493 Label divisor_is_number(this), 1494 divisor_is_not_number(this, Label::kDeferred); 1495 Branch(IsHeapNumberMap(divisor_map), &divisor_is_number, 1496 &divisor_is_not_number); 1497 1498 Bind(&divisor_is_number); 1499 { 1500 // Both {dividend} and {divisor} are HeapNumbers. Load their values 1501 // and compute their modulus. 1502 var_dividend_float64.Bind(LoadHeapNumberValue(dividend)); 1503 var_divisor_float64.Bind(LoadHeapNumberValue(divisor)); 1504 Goto(&do_fmod); 1505 } 1506 1507 Bind(&divisor_is_not_number); 1508 { 1509 // Convert {divisor} to a number and loop. 1510 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 1511 var_divisor.Bind(CallStub(callable, context, divisor)); 1512 Goto(&loop); 1513 } 1514 } 1515 } 1516 1517 Bind(÷nd_is_not_number); 1518 { 1519 // Convert {dividend} to a Number and loop. 1520 Callable callable = CodeFactory::NonNumberToNumber(isolate()); 1521 var_dividend.Bind(CallStub(callable, context, dividend)); 1522 Goto(&loop); 1523 } 1524 } 1525 } 1526 1527 Bind(&do_fmod); 1528 { 1529 Node* value = 1530 Float64Mod(var_dividend_float64.value(), var_divisor_float64.value()); 1531 var_result.Bind(AllocateHeapNumberWithValue(value)); 1532 Goto(&return_result); 1533 } 1534 1535 Bind(&return_result); 1536 Return(var_result.value()); 1537 } 1538 1539 TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) { 1540 BitwiseShiftOp([this](Node* lhs, Node* shift_count) { 1541 return Word32Shl(lhs, shift_count); 1542 }); 1543 } 1544 1545 TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) { 1546 BitwiseShiftOp([this](Node* lhs, Node* shift_count) { 1547 return Word32Sar(lhs, shift_count); 1548 }); 1549 } 1550 1551 TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) { 1552 BitwiseShiftOp<kUnsigned>([this](Node* lhs, Node* shift_count) { 1553 return Word32Shr(lhs, shift_count); 1554 }); 1555 } 1556 1557 TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) { 1558 BitwiseOp([this](Node* lhs, Node* rhs) { return Word32And(lhs, rhs); }); 1559 } 1560 1561 TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) { 1562 BitwiseOp([this](Node* lhs, Node* rhs) { return Word32Or(lhs, rhs); }); 1563 } 1564 1565 TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) { 1566 BitwiseOp([this](Node* lhs, Node* rhs) { return Word32Xor(lhs, rhs); }); 1567 } 1568 1569 TF_BUILTIN(LessThan, NumberBuiltinsAssembler) { 1570 RelationalComparisonBuiltin(kLessThan); 1571 } 1572 1573 TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) { 1574 RelationalComparisonBuiltin(kLessThanOrEqual); 1575 } 1576 1577 TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) { 1578 RelationalComparisonBuiltin(kGreaterThan); 1579 } 1580 1581 TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) { 1582 RelationalComparisonBuiltin(kGreaterThanOrEqual); 1583 } 1584 1585 TF_BUILTIN(Equal, CodeStubAssembler) { 1586 Node* lhs = Parameter(0); 1587 Node* rhs = Parameter(1); 1588 Node* context = Parameter(2); 1589 1590 Return(Equal(kDontNegateResult, lhs, rhs, context)); 1591 } 1592 1593 TF_BUILTIN(NotEqual, CodeStubAssembler) { 1594 Node* lhs = Parameter(0); 1595 Node* rhs = Parameter(1); 1596 Node* context = Parameter(2); 1597 1598 Return(Equal(kNegateResult, lhs, rhs, context)); 1599 } 1600 1601 TF_BUILTIN(StrictEqual, CodeStubAssembler) { 1602 Node* lhs = Parameter(0); 1603 Node* rhs = Parameter(1); 1604 Node* context = Parameter(2); 1605 1606 Return(StrictEqual(kDontNegateResult, lhs, rhs, context)); 1607 } 1608 1609 TF_BUILTIN(StrictNotEqual, CodeStubAssembler) { 1610 Node* lhs = Parameter(0); 1611 Node* rhs = Parameter(1); 1612 Node* context = Parameter(2); 1613 1614 Return(StrictEqual(kNegateResult, lhs, rhs, context)); 1615 } 1616 1617 } // namespace internal 1618 } // namespace v8 1619