1 // Copyright 2017 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-math-gen.h" 6 #include "src/builtins/builtins-utils-gen.h" 7 #include "src/builtins/builtins.h" 8 #include "src/code-stub-assembler.h" 9 #include "src/ic/binary-op-assembler.h" 10 11 namespace v8 { 12 namespace internal { 13 14 // ----------------------------------------------------------------------------- 15 // ES6 section 20.1 Number Objects 16 17 class NumberBuiltinsAssembler : public CodeStubAssembler { 18 public: 19 explicit NumberBuiltinsAssembler(compiler::CodeAssemblerState* state) 20 : CodeStubAssembler(state) {} 21 22 protected: 23 template <typename Descriptor> 24 void EmitBitwiseOp(Operation op) { 25 Node* left = Parameter(Descriptor::kLeft); 26 Node* right = Parameter(Descriptor::kRight); 27 Node* context = Parameter(Descriptor::kContext); 28 29 VARIABLE(var_left_word32, MachineRepresentation::kWord32); 30 VARIABLE(var_right_word32, MachineRepresentation::kWord32); 31 VARIABLE(var_left_bigint, MachineRepresentation::kTagged, left); 32 VARIABLE(var_right_bigint, MachineRepresentation::kTagged); 33 Label if_left_number(this), do_number_op(this); 34 Label if_left_bigint(this), do_bigint_op(this); 35 36 TaggedToWord32OrBigInt(context, left, &if_left_number, &var_left_word32, 37 &if_left_bigint, &var_left_bigint); 38 BIND(&if_left_number); 39 TaggedToWord32OrBigInt(context, right, &do_number_op, &var_right_word32, 40 &do_bigint_op, &var_right_bigint); 41 BIND(&do_number_op); 42 Return(BitwiseOp(var_left_word32.value(), var_right_word32.value(), op)); 43 44 // BigInt cases. 45 BIND(&if_left_bigint); 46 TaggedToNumeric(context, right, &do_bigint_op, &var_right_bigint); 47 48 BIND(&do_bigint_op); 49 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, 50 var_left_bigint.value(), var_right_bigint.value(), 51 SmiConstant(op))); 52 } 53 54 template <typename Descriptor> 55 void RelationalComparisonBuiltin(Operation op) { 56 Node* lhs = Parameter(Descriptor::kLeft); 57 Node* rhs = Parameter(Descriptor::kRight); 58 Node* context = Parameter(Descriptor::kContext); 59 60 Return(RelationalComparison(op, lhs, rhs, context)); 61 } 62 63 template <typename Descriptor> 64 void UnaryOp(Variable* var_input, Label* do_smi, Label* do_double, 65 Variable* var_input_double, Label* do_bigint); 66 67 template <typename Descriptor> 68 void BinaryOp(Label* smis, Variable* var_left, Variable* var_right, 69 Label* doubles, Variable* var_left_double, 70 Variable* var_right_double, Label* bigints); 71 }; 72 73 // ES6 #sec-number.isfinite 74 TF_BUILTIN(NumberIsFinite, CodeStubAssembler) { 75 Node* number = Parameter(Descriptor::kNumber); 76 77 Label return_true(this), return_false(this); 78 79 // Check if {number} is a Smi. 80 GotoIf(TaggedIsSmi(number), &return_true); 81 82 // Check if {number} is a HeapNumber. 83 GotoIfNot(IsHeapNumber(number), &return_false); 84 85 // Check if {number} contains a finite, non-NaN value. 86 Node* number_value = LoadHeapNumberValue(number); 87 BranchIfFloat64IsNaN(Float64Sub(number_value, number_value), &return_false, 88 &return_true); 89 90 BIND(&return_true); 91 Return(TrueConstant()); 92 93 BIND(&return_false); 94 Return(FalseConstant()); 95 } 96 97 TF_BUILTIN(AllocateHeapNumber, CodeStubAssembler) { 98 Node* result = AllocateHeapNumber(); 99 Return(result); 100 } 101 102 // ES6 #sec-number.isinteger 103 TF_BUILTIN(NumberIsInteger, CodeStubAssembler) { 104 TNode<Object> number = CAST(Parameter(Descriptor::kNumber)); 105 Return(SelectBooleanConstant(IsInteger(number))); 106 } 107 108 // ES6 #sec-number.isnan 109 TF_BUILTIN(NumberIsNaN, CodeStubAssembler) { 110 Node* number = Parameter(Descriptor::kNumber); 111 112 Label return_true(this), return_false(this); 113 114 // Check if {number} is a Smi. 115 GotoIf(TaggedIsSmi(number), &return_false); 116 117 // Check if {number} is a HeapNumber. 118 GotoIfNot(IsHeapNumber(number), &return_false); 119 120 // Check if {number} contains a NaN value. 121 Node* number_value = LoadHeapNumberValue(number); 122 BranchIfFloat64IsNaN(number_value, &return_true, &return_false); 123 124 BIND(&return_true); 125 Return(TrueConstant()); 126 127 BIND(&return_false); 128 Return(FalseConstant()); 129 } 130 131 // ES6 #sec-number.issafeinteger 132 TF_BUILTIN(NumberIsSafeInteger, CodeStubAssembler) { 133 TNode<Object> number = CAST(Parameter(Descriptor::kNumber)); 134 Return(SelectBooleanConstant(IsSafeInteger(number))); 135 } 136 137 // ES6 #sec-number.parsefloat 138 TF_BUILTIN(NumberParseFloat, CodeStubAssembler) { 139 Node* context = Parameter(Descriptor::kContext); 140 141 // We might need to loop once for ToString conversion. 142 VARIABLE(var_input, MachineRepresentation::kTagged, 143 Parameter(Descriptor::kString)); 144 Label loop(this, &var_input); 145 Goto(&loop); 146 BIND(&loop); 147 { 148 // Load the current {input} value. 149 Node* input = var_input.value(); 150 151 // Check if the {input} is a HeapObject or a Smi. 152 Label if_inputissmi(this), if_inputisnotsmi(this); 153 Branch(TaggedIsSmi(input), &if_inputissmi, &if_inputisnotsmi); 154 155 BIND(&if_inputissmi); 156 { 157 // The {input} is already a Number, no need to do anything. 158 Return(input); 159 } 160 161 BIND(&if_inputisnotsmi); 162 { 163 // The {input} is a HeapObject, check if it's already a String. 164 Label if_inputisstring(this), if_inputisnotstring(this); 165 Node* input_map = LoadMap(input); 166 Node* input_instance_type = LoadMapInstanceType(input_map); 167 Branch(IsStringInstanceType(input_instance_type), &if_inputisstring, 168 &if_inputisnotstring); 169 170 BIND(&if_inputisstring); 171 { 172 // The {input} is already a String, check if {input} contains 173 // a cached array index. 174 Label if_inputcached(this), if_inputnotcached(this); 175 Node* input_hash = LoadNameHashField(input); 176 Branch(IsClearWord32(input_hash, 177 Name::kDoesNotContainCachedArrayIndexMask), 178 &if_inputcached, &if_inputnotcached); 179 180 BIND(&if_inputcached); 181 { 182 // Just return the {input}s cached array index. 183 Node* input_array_index = 184 DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash); 185 Return(SmiTag(input_array_index)); 186 } 187 188 BIND(&if_inputnotcached); 189 { 190 // Need to fall back to the runtime to convert {input} to double. 191 Return(CallRuntime(Runtime::kStringParseFloat, context, input)); 192 } 193 } 194 195 BIND(&if_inputisnotstring); 196 { 197 // The {input} is neither a String nor a Smi, check for HeapNumber. 198 Label if_inputisnumber(this), 199 if_inputisnotnumber(this, Label::kDeferred); 200 Branch(IsHeapNumberMap(input_map), &if_inputisnumber, 201 &if_inputisnotnumber); 202 203 BIND(&if_inputisnumber); 204 { 205 // The {input} is already a Number, take care of -0. 206 Label if_inputiszero(this), if_inputisnotzero(this); 207 Node* input_value = LoadHeapNumberValue(input); 208 Branch(Float64Equal(input_value, Float64Constant(0.0)), 209 &if_inputiszero, &if_inputisnotzero); 210 211 BIND(&if_inputiszero); 212 Return(SmiConstant(0)); 213 214 BIND(&if_inputisnotzero); 215 Return(input); 216 } 217 218 BIND(&if_inputisnotnumber); 219 { 220 // Need to convert the {input} to String first. 221 // TODO(bmeurer): This could be more efficient if necessary. 222 var_input.Bind(CallBuiltin(Builtins::kToString, context, input)); 223 Goto(&loop); 224 } 225 } 226 } 227 } 228 } 229 230 // ES6 #sec-number.parseint 231 TF_BUILTIN(ParseInt, CodeStubAssembler) { 232 Node* context = Parameter(Descriptor::kContext); 233 Node* input = Parameter(Descriptor::kString); 234 Node* radix = Parameter(Descriptor::kRadix); 235 236 // Check if {radix} is treated as 10 (i.e. undefined, 0 or 10). 237 Label if_radix10(this), if_generic(this, Label::kDeferred); 238 GotoIf(IsUndefined(radix), &if_radix10); 239 GotoIf(WordEqual(radix, SmiConstant(10)), &if_radix10); 240 GotoIf(WordEqual(radix, SmiConstant(0)), &if_radix10); 241 Goto(&if_generic); 242 243 BIND(&if_radix10); 244 { 245 // Check if we can avoid the ToString conversion on {input}. 246 Label if_inputissmi(this), if_inputisheapnumber(this), 247 if_inputisstring(this); 248 GotoIf(TaggedIsSmi(input), &if_inputissmi); 249 Node* input_map = LoadMap(input); 250 GotoIf(IsHeapNumberMap(input_map), &if_inputisheapnumber); 251 Node* input_instance_type = LoadMapInstanceType(input_map); 252 Branch(IsStringInstanceType(input_instance_type), &if_inputisstring, 253 &if_generic); 254 255 BIND(&if_inputissmi); 256 { 257 // Just return the {input}. 258 Return(input); 259 } 260 261 BIND(&if_inputisheapnumber); 262 { 263 // Check if the {input} value is in Signed32 range. 264 Label if_inputissigned32(this); 265 Node* input_value = LoadHeapNumberValue(input); 266 Node* input_value32 = TruncateFloat64ToWord32(input_value); 267 GotoIf(Float64Equal(input_value, ChangeInt32ToFloat64(input_value32)), 268 &if_inputissigned32); 269 270 // Check if the absolute {input} value is in the [1,1<<31[ range. 271 // Take the generic path for the range [0,1[ because the result 272 // could be -0. 273 Node* input_value_abs = Float64Abs(input_value); 274 275 GotoIfNot(Float64LessThan(input_value_abs, Float64Constant(1u << 31)), 276 &if_generic); 277 Branch(Float64LessThanOrEqual(Float64Constant(1), input_value_abs), 278 &if_inputissigned32, &if_generic); 279 280 // Return the truncated int32 value, and return the tagged result. 281 BIND(&if_inputissigned32); 282 Node* result = ChangeInt32ToTagged(input_value32); 283 Return(result); 284 } 285 286 BIND(&if_inputisstring); 287 { 288 // Check if the String {input} has a cached array index. 289 Node* input_hash = LoadNameHashField(input); 290 GotoIf(IsSetWord32(input_hash, Name::kDoesNotContainCachedArrayIndexMask), 291 &if_generic); 292 293 // Return the cached array index as result. 294 Node* input_index = 295 DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash); 296 Node* result = SmiTag(input_index); 297 Return(result); 298 } 299 } 300 301 BIND(&if_generic); 302 { 303 Node* result = CallRuntime(Runtime::kStringParseInt, context, input, radix); 304 Return(result); 305 } 306 } 307 308 // ES6 #sec-number.parseint 309 TF_BUILTIN(NumberParseInt, CodeStubAssembler) { 310 Node* context = Parameter(Descriptor::kContext); 311 Node* input = Parameter(Descriptor::kString); 312 Node* radix = Parameter(Descriptor::kRadix); 313 Return(CallBuiltin(Builtins::kParseInt, context, input, radix)); 314 } 315 316 // ES6 #sec-number.prototype.valueof 317 TF_BUILTIN(NumberPrototypeValueOf, CodeStubAssembler) { 318 Node* context = Parameter(Descriptor::kContext); 319 Node* receiver = Parameter(Descriptor::kReceiver); 320 321 Node* result = ToThisValue(context, receiver, PrimitiveType::kNumber, 322 "Number.prototype.valueOf"); 323 Return(result); 324 } 325 326 class AddStubAssembler : public CodeStubAssembler { 327 public: 328 explicit AddStubAssembler(compiler::CodeAssemblerState* state) 329 : CodeStubAssembler(state) {} 330 331 protected: 332 void ConvertReceiverAndLoop(Variable* var_value, Label* loop, Node* context) { 333 // Call ToPrimitive explicitly without hint (whereas ToNumber 334 // would pass a "number" hint). 335 Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate()); 336 var_value->Bind(CallStub(callable, context, var_value->value())); 337 Goto(loop); 338 } 339 340 void ConvertNonReceiverAndLoop(Variable* var_value, Label* loop, 341 Node* context) { 342 var_value->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context, 343 var_value->value())); 344 Goto(loop); 345 } 346 347 void ConvertAndLoop(Variable* var_value, Node* instance_type, Label* loop, 348 Node* context) { 349 Label is_not_receiver(this, Label::kDeferred); 350 GotoIfNot(IsJSReceiverInstanceType(instance_type), &is_not_receiver); 351 352 ConvertReceiverAndLoop(var_value, loop, context); 353 354 BIND(&is_not_receiver); 355 ConvertNonReceiverAndLoop(var_value, loop, context); 356 } 357 }; 358 359 TF_BUILTIN(Add, AddStubAssembler) { 360 Node* context = Parameter(Descriptor::kContext); 361 VARIABLE(var_left, MachineRepresentation::kTagged, 362 Parameter(Descriptor::kLeft)); 363 VARIABLE(var_right, MachineRepresentation::kTagged, 364 Parameter(Descriptor::kRight)); 365 366 // Shared entry for floating point addition. 367 Label do_double_add(this); 368 VARIABLE(var_left_double, MachineRepresentation::kFloat64); 369 VARIABLE(var_right_double, MachineRepresentation::kFloat64); 370 371 // We might need to loop several times due to ToPrimitive, ToString and/or 372 // ToNumeric conversions. 373 VARIABLE(var_result, MachineRepresentation::kTagged); 374 Variable* loop_vars[2] = {&var_left, &var_right}; 375 Label loop(this, 2, loop_vars), 376 string_add_convert_left(this, Label::kDeferred), 377 string_add_convert_right(this, Label::kDeferred), 378 do_bigint_add(this, Label::kDeferred); 379 Goto(&loop); 380 BIND(&loop); 381 { 382 Node* left = var_left.value(); 383 Node* right = var_right.value(); 384 385 Label if_left_smi(this), if_left_heapobject(this); 386 Branch(TaggedIsSmi(left), &if_left_smi, &if_left_heapobject); 387 388 BIND(&if_left_smi); 389 { 390 Label if_right_smi(this), if_right_heapobject(this); 391 Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject); 392 393 BIND(&if_right_smi); 394 { 395 Label if_overflow(this); 396 TNode<Smi> result = TrySmiAdd(CAST(left), CAST(right), &if_overflow); 397 Return(result); 398 399 BIND(&if_overflow); 400 { 401 var_left_double.Bind(SmiToFloat64(left)); 402 var_right_double.Bind(SmiToFloat64(right)); 403 Goto(&do_double_add); 404 } 405 } // if_right_smi 406 407 BIND(&if_right_heapobject); 408 { 409 Node* right_map = LoadMap(right); 410 411 Label if_right_not_number(this, Label::kDeferred); 412 GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number); 413 414 // {right} is a HeapNumber. 415 var_left_double.Bind(SmiToFloat64(left)); 416 var_right_double.Bind(LoadHeapNumberValue(right)); 417 Goto(&do_double_add); 418 419 BIND(&if_right_not_number); 420 { 421 Node* right_instance_type = LoadMapInstanceType(right_map); 422 GotoIf(IsStringInstanceType(right_instance_type), 423 &string_add_convert_left); 424 GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add); 425 ConvertAndLoop(&var_right, right_instance_type, &loop, context); 426 } 427 } // if_right_heapobject 428 } // if_left_smi 429 430 BIND(&if_left_heapobject); 431 { 432 Node* left_map = LoadMap(left); 433 Label if_right_smi(this), if_right_heapobject(this); 434 Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject); 435 436 BIND(&if_right_smi); 437 { 438 Label if_left_not_number(this, Label::kDeferred); 439 GotoIfNot(IsHeapNumberMap(left_map), &if_left_not_number); 440 441 // {left} is a HeapNumber, {right} is a Smi. 442 var_left_double.Bind(LoadHeapNumberValue(left)); 443 var_right_double.Bind(SmiToFloat64(right)); 444 Goto(&do_double_add); 445 446 BIND(&if_left_not_number); 447 { 448 Node* left_instance_type = LoadMapInstanceType(left_map); 449 GotoIf(IsStringInstanceType(left_instance_type), 450 &string_add_convert_right); 451 GotoIf(IsBigIntInstanceType(left_instance_type), &do_bigint_add); 452 // {left} is neither a Numeric nor a String, and {right} is a Smi. 453 ConvertAndLoop(&var_left, left_instance_type, &loop, context); 454 } 455 } // if_right_smi 456 457 BIND(&if_right_heapobject); 458 { 459 Node* right_map = LoadMap(right); 460 461 Label if_left_number(this), if_left_not_number(this, Label::kDeferred); 462 Branch(IsHeapNumberMap(left_map), &if_left_number, &if_left_not_number); 463 464 BIND(&if_left_number); 465 { 466 Label if_right_not_number(this, Label::kDeferred); 467 GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number); 468 469 // Both {left} and {right} are HeapNumbers. 470 var_left_double.Bind(LoadHeapNumberValue(left)); 471 var_right_double.Bind(LoadHeapNumberValue(right)); 472 Goto(&do_double_add); 473 474 BIND(&if_right_not_number); 475 { 476 Node* right_instance_type = LoadMapInstanceType(right_map); 477 GotoIf(IsStringInstanceType(right_instance_type), 478 &string_add_convert_left); 479 GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add); 480 // {left} is a HeapNumber, {right} is neither Number nor String. 481 ConvertAndLoop(&var_right, right_instance_type, &loop, context); 482 } 483 } // if_left_number 484 485 BIND(&if_left_not_number); 486 { 487 Label if_left_bigint(this); 488 Node* left_instance_type = LoadMapInstanceType(left_map); 489 GotoIf(IsStringInstanceType(left_instance_type), 490 &string_add_convert_right); 491 Node* right_instance_type = LoadMapInstanceType(right_map); 492 GotoIf(IsStringInstanceType(right_instance_type), 493 &string_add_convert_left); 494 GotoIf(IsBigIntInstanceType(left_instance_type), &if_left_bigint); 495 Label if_left_not_receiver(this, Label::kDeferred); 496 Label if_right_not_receiver(this, Label::kDeferred); 497 GotoIfNot(IsJSReceiverInstanceType(left_instance_type), 498 &if_left_not_receiver); 499 // {left} is a JSReceiver, convert it first. 500 ConvertReceiverAndLoop(&var_left, &loop, context); 501 502 BIND(&if_left_bigint); 503 { 504 // {right} is a HeapObject, but not a String. Jump to 505 // {do_bigint_add} if {right} is already a Numeric. 506 GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add); 507 GotoIf(IsHeapNumberMap(right_map), &do_bigint_add); 508 ConvertAndLoop(&var_right, right_instance_type, &loop, context); 509 } 510 511 BIND(&if_left_not_receiver); 512 GotoIfNot(IsJSReceiverInstanceType(right_instance_type), 513 &if_right_not_receiver); 514 // {left} is a Primitive, but {right} is a JSReceiver, so convert 515 // {right} with priority. 516 ConvertReceiverAndLoop(&var_right, &loop, context); 517 518 BIND(&if_right_not_receiver); 519 // Neither {left} nor {right} are JSReceivers. 520 ConvertNonReceiverAndLoop(&var_left, &loop, context); 521 } 522 } // if_right_heapobject 523 } // if_left_heapobject 524 } 525 BIND(&string_add_convert_left); 526 { 527 // Convert {left} to a String and concatenate it with the String {right}. 528 Callable callable = 529 CodeFactory::StringAdd(isolate(), STRING_ADD_CONVERT_LEFT, NOT_TENURED); 530 Return(CallStub(callable, context, var_left.value(), var_right.value())); 531 } 532 533 BIND(&string_add_convert_right); 534 { 535 // Convert {right} to a String and concatenate it with the String {left}. 536 Callable callable = CodeFactory::StringAdd( 537 isolate(), STRING_ADD_CONVERT_RIGHT, NOT_TENURED); 538 Return(CallStub(callable, context, var_left.value(), var_right.value())); 539 } 540 541 BIND(&do_bigint_add); 542 { 543 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), 544 var_right.value(), SmiConstant(Operation::kAdd))); 545 } 546 547 BIND(&do_double_add); 548 { 549 Node* value = Float64Add(var_left_double.value(), var_right_double.value()); 550 Return(AllocateHeapNumberWithValue(value)); 551 } 552 } 553 554 template <typename Descriptor> 555 void NumberBuiltinsAssembler::UnaryOp(Variable* var_input, Label* do_smi, 556 Label* do_double, 557 Variable* var_input_double, 558 Label* do_bigint) { 559 DCHECK_EQ(var_input->rep(), MachineRepresentation::kTagged); 560 DCHECK_IMPLIES(var_input_double != nullptr, 561 var_input_double->rep() == MachineRepresentation::kFloat64); 562 563 Node* context = Parameter(Descriptor::kContext); 564 var_input->Bind(Parameter(Descriptor::kValue)); 565 566 // We might need to loop for ToNumeric conversion. 567 Label loop(this, {var_input}); 568 Goto(&loop); 569 BIND(&loop); 570 Node* input = var_input->value(); 571 572 Label not_number(this); 573 GotoIf(TaggedIsSmi(input), do_smi); 574 GotoIfNot(IsHeapNumber(input), ¬_number); 575 if (var_input_double != nullptr) { 576 var_input_double->Bind(LoadHeapNumberValue(input)); 577 } 578 Goto(do_double); 579 580 BIND(¬_number); 581 GotoIf(IsBigInt(input), do_bigint); 582 var_input->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context, input)); 583 Goto(&loop); 584 } 585 586 template <typename Descriptor> 587 void NumberBuiltinsAssembler::BinaryOp(Label* smis, Variable* var_left, 588 Variable* var_right, Label* doubles, 589 Variable* var_left_double, 590 Variable* var_right_double, 591 Label* bigints) { 592 DCHECK_EQ(var_left->rep(), MachineRepresentation::kTagged); 593 DCHECK_EQ(var_right->rep(), MachineRepresentation::kTagged); 594 DCHECK_IMPLIES(var_left_double != nullptr, 595 var_left_double->rep() == MachineRepresentation::kFloat64); 596 DCHECK_IMPLIES(var_right_double != nullptr, 597 var_right_double->rep() == MachineRepresentation::kFloat64); 598 DCHECK_EQ(var_left_double == nullptr, var_right_double == nullptr); 599 600 Node* context = Parameter(Descriptor::kContext); 601 var_left->Bind(Parameter(Descriptor::kLeft)); 602 var_right->Bind(Parameter(Descriptor::kRight)); 603 604 // We might need to loop for ToNumeric conversions. 605 Label loop(this, {var_left, var_right}); 606 Goto(&loop); 607 BIND(&loop); 608 609 Label left_not_smi(this), right_not_smi(this); 610 Label left_not_number(this), right_not_number(this); 611 GotoIfNot(TaggedIsSmi(var_left->value()), &left_not_smi); 612 GotoIf(TaggedIsSmi(var_right->value()), smis); 613 614 // At this point, var_left is a Smi but var_right is not. 615 GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number); 616 if (var_left_double != nullptr) { 617 var_left_double->Bind(SmiToFloat64(var_left->value())); 618 var_right_double->Bind(LoadHeapNumberValue(var_right->value())); 619 } 620 Goto(doubles); 621 622 BIND(&left_not_smi); 623 { 624 GotoIfNot(IsHeapNumber(var_left->value()), &left_not_number); 625 GotoIfNot(TaggedIsSmi(var_right->value()), &right_not_smi); 626 627 // At this point, var_left is a HeapNumber and var_right is a Smi. 628 if (var_left_double != nullptr) { 629 var_left_double->Bind(LoadHeapNumberValue(var_left->value())); 630 var_right_double->Bind(SmiToFloat64(var_right->value())); 631 } 632 Goto(doubles); 633 } 634 635 BIND(&right_not_smi); 636 { 637 GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number); 638 if (var_left_double != nullptr) { 639 var_left_double->Bind(LoadHeapNumberValue(var_left->value())); 640 var_right_double->Bind(LoadHeapNumberValue(var_right->value())); 641 } 642 Goto(doubles); 643 } 644 645 BIND(&left_not_number); 646 { 647 Label left_bigint(this); 648 GotoIf(IsBigInt(var_left->value()), &left_bigint); 649 var_left->Bind( 650 CallBuiltin(Builtins::kNonNumberToNumeric, context, var_left->value())); 651 Goto(&loop); 652 653 BIND(&left_bigint); 654 { 655 // Jump to {bigints} if {var_right} is already a Numeric. 656 GotoIf(TaggedIsSmi(var_right->value()), bigints); 657 GotoIf(IsBigInt(var_right->value()), bigints); 658 GotoIf(IsHeapNumber(var_right->value()), bigints); 659 var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context, 660 var_right->value())); 661 Goto(&loop); 662 } 663 } 664 665 BIND(&right_not_number); 666 { 667 GotoIf(IsBigInt(var_right->value()), bigints); 668 var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context, 669 var_right->value())); 670 Goto(&loop); 671 } 672 } 673 674 TF_BUILTIN(Subtract, NumberBuiltinsAssembler) { 675 VARIABLE(var_left, MachineRepresentation::kTagged); 676 VARIABLE(var_right, MachineRepresentation::kTagged); 677 VARIABLE(var_left_double, MachineRepresentation::kFloat64); 678 VARIABLE(var_right_double, MachineRepresentation::kFloat64); 679 Label do_smi_sub(this), do_double_sub(this), do_bigint_sub(this); 680 681 BinaryOp<Descriptor>(&do_smi_sub, &var_left, &var_right, &do_double_sub, 682 &var_left_double, &var_right_double, &do_bigint_sub); 683 684 BIND(&do_smi_sub); 685 { 686 Label if_overflow(this); 687 TNode<Smi> result = TrySmiSub(CAST(var_left.value()), 688 CAST(var_right.value()), &if_overflow); 689 Return(result); 690 691 BIND(&if_overflow); 692 { 693 var_left_double.Bind(SmiToFloat64(var_left.value())); 694 var_right_double.Bind(SmiToFloat64(var_right.value())); 695 Goto(&do_double_sub); 696 } 697 } 698 699 BIND(&do_double_sub); 700 { 701 Node* value = Float64Sub(var_left_double.value(), var_right_double.value()); 702 Return(AllocateHeapNumberWithValue(value)); 703 } 704 705 BIND(&do_bigint_sub); 706 { 707 Node* context = Parameter(Descriptor::kContext); 708 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), 709 var_right.value(), SmiConstant(Operation::kSubtract))); 710 } 711 } 712 713 TF_BUILTIN(BitwiseNot, NumberBuiltinsAssembler) { 714 Node* context = Parameter(Descriptor::kContext); 715 VARIABLE(var_input, MachineRepresentation::kTagged); 716 Label do_number(this), do_bigint(this); 717 718 UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint); 719 720 BIND(&do_number); 721 { 722 TailCallBuiltin(Builtins::kBitwiseXor, context, var_input.value(), 723 SmiConstant(-1)); 724 } 725 726 BIND(&do_bigint); 727 { 728 Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(), 729 SmiConstant(Operation::kBitwiseNot))); 730 } 731 } 732 733 TF_BUILTIN(Decrement, NumberBuiltinsAssembler) { 734 Node* context = Parameter(Descriptor::kContext); 735 VARIABLE(var_input, MachineRepresentation::kTagged); 736 Label do_number(this), do_bigint(this); 737 738 UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint); 739 740 BIND(&do_number); 741 { 742 TailCallBuiltin(Builtins::kSubtract, context, var_input.value(), 743 SmiConstant(1)); 744 } 745 746 BIND(&do_bigint); 747 { 748 Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(), 749 SmiConstant(Operation::kDecrement))); 750 } 751 } 752 753 TF_BUILTIN(Increment, NumberBuiltinsAssembler) { 754 Node* context = Parameter(Descriptor::kContext); 755 VARIABLE(var_input, MachineRepresentation::kTagged); 756 Label do_number(this), do_bigint(this); 757 758 UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint); 759 760 BIND(&do_number); 761 { 762 TailCallBuiltin(Builtins::kAdd, context, var_input.value(), SmiConstant(1)); 763 } 764 765 BIND(&do_bigint); 766 { 767 Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(), 768 SmiConstant(Operation::kIncrement))); 769 } 770 } 771 772 TF_BUILTIN(Negate, NumberBuiltinsAssembler) { 773 VARIABLE(var_input, MachineRepresentation::kTagged); 774 VARIABLE(var_input_double, MachineRepresentation::kFloat64); 775 Label do_smi(this), do_double(this), do_bigint(this); 776 777 UnaryOp<Descriptor>(&var_input, &do_smi, &do_double, &var_input_double, 778 &do_bigint); 779 780 BIND(&do_smi); 781 { Return(SmiMul(CAST(var_input.value()), SmiConstant(-1))); } 782 783 BIND(&do_double); 784 { 785 Node* value = Float64Mul(var_input_double.value(), Float64Constant(-1)); 786 Return(AllocateHeapNumberWithValue(value)); 787 } 788 789 BIND(&do_bigint); 790 { 791 Node* context = Parameter(Descriptor::kContext); 792 Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(), 793 SmiConstant(Operation::kNegate))); 794 } 795 } 796 797 TF_BUILTIN(Multiply, NumberBuiltinsAssembler) { 798 VARIABLE(var_left, MachineRepresentation::kTagged); 799 VARIABLE(var_right, MachineRepresentation::kTagged); 800 VARIABLE(var_left_double, MachineRepresentation::kFloat64); 801 VARIABLE(var_right_double, MachineRepresentation::kFloat64); 802 Label do_smi_mul(this), do_double_mul(this), do_bigint_mul(this); 803 804 BinaryOp<Descriptor>(&do_smi_mul, &var_left, &var_right, &do_double_mul, 805 &var_left_double, &var_right_double, &do_bigint_mul); 806 807 BIND(&do_smi_mul); 808 // The result is not necessarily a smi, in case of overflow. 809 Return(SmiMul(CAST(var_left.value()), CAST(var_right.value()))); 810 811 BIND(&do_double_mul); 812 Node* value = Float64Mul(var_left_double.value(), var_right_double.value()); 813 Return(AllocateHeapNumberWithValue(value)); 814 815 BIND(&do_bigint_mul); 816 { 817 Node* context = Parameter(Descriptor::kContext); 818 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), 819 var_right.value(), SmiConstant(Operation::kMultiply))); 820 } 821 } 822 823 TF_BUILTIN(Divide, NumberBuiltinsAssembler) { 824 VARIABLE(var_left, MachineRepresentation::kTagged); 825 VARIABLE(var_right, MachineRepresentation::kTagged); 826 VARIABLE(var_left_double, MachineRepresentation::kFloat64); 827 VARIABLE(var_right_double, MachineRepresentation::kFloat64); 828 Label do_smi_div(this), do_double_div(this), do_bigint_div(this); 829 830 BinaryOp<Descriptor>(&do_smi_div, &var_left, &var_right, &do_double_div, 831 &var_left_double, &var_right_double, &do_bigint_div); 832 833 BIND(&do_smi_div); 834 { 835 // TODO(jkummerow): Consider just always doing a double division. 836 Label bailout(this); 837 TNode<Smi> dividend = CAST(var_left.value()); 838 TNode<Smi> divisor = CAST(var_right.value()); 839 840 // Do floating point division if {divisor} is zero. 841 GotoIf(SmiEqual(divisor, SmiConstant(0)), &bailout); 842 843 // Do floating point division if {dividend} is zero and {divisor} is 844 // negative. 845 Label dividend_is_zero(this), dividend_is_not_zero(this); 846 Branch(SmiEqual(dividend, SmiConstant(0)), ÷nd_is_zero, 847 ÷nd_is_not_zero); 848 849 BIND(÷nd_is_zero); 850 { 851 GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout); 852 Goto(÷nd_is_not_zero); 853 } 854 BIND(÷nd_is_not_zero); 855 856 Node* untagged_divisor = SmiToInt32(divisor); 857 Node* untagged_dividend = SmiToInt32(dividend); 858 859 // Do floating point division if {dividend} is kMinInt (or kMinInt - 1 860 // if the Smi size is 31) and {divisor} is -1. 861 Label divisor_is_minus_one(this), divisor_is_not_minus_one(this); 862 Branch(Word32Equal(untagged_divisor, Int32Constant(-1)), 863 &divisor_is_minus_one, &divisor_is_not_minus_one); 864 865 BIND(&divisor_is_minus_one); 866 { 867 GotoIf(Word32Equal( 868 untagged_dividend, 869 Int32Constant(kSmiValueSize == 32 ? kMinInt : (kMinInt >> 1))), 870 &bailout); 871 Goto(&divisor_is_not_minus_one); 872 } 873 BIND(&divisor_is_not_minus_one); 874 875 // TODO(epertoso): consider adding a machine instruction that returns 876 // both the result and the remainder. 877 Node* untagged_result = Int32Div(untagged_dividend, untagged_divisor); 878 Node* truncated = Int32Mul(untagged_result, untagged_divisor); 879 // Do floating point division if the remainder is not 0. 880 GotoIf(Word32NotEqual(untagged_dividend, truncated), &bailout); 881 Return(SmiFromInt32(untagged_result)); 882 883 // Bailout: convert {dividend} and {divisor} to double and do double 884 // division. 885 BIND(&bailout); 886 { 887 var_left_double.Bind(SmiToFloat64(dividend)); 888 var_right_double.Bind(SmiToFloat64(divisor)); 889 Goto(&do_double_div); 890 } 891 } 892 893 BIND(&do_double_div); 894 { 895 Node* value = Float64Div(var_left_double.value(), var_right_double.value()); 896 Return(AllocateHeapNumberWithValue(value)); 897 } 898 899 BIND(&do_bigint_div); 900 { 901 Node* context = Parameter(Descriptor::kContext); 902 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), 903 var_right.value(), SmiConstant(Operation::kDivide))); 904 } 905 } 906 907 TF_BUILTIN(Modulus, NumberBuiltinsAssembler) { 908 VARIABLE(var_left, MachineRepresentation::kTagged); 909 VARIABLE(var_right, MachineRepresentation::kTagged); 910 VARIABLE(var_left_double, MachineRepresentation::kFloat64); 911 VARIABLE(var_right_double, MachineRepresentation::kFloat64); 912 Label do_smi_mod(this), do_double_mod(this), do_bigint_mod(this); 913 914 BinaryOp<Descriptor>(&do_smi_mod, &var_left, &var_right, &do_double_mod, 915 &var_left_double, &var_right_double, &do_bigint_mod); 916 917 BIND(&do_smi_mod); 918 Return(SmiMod(CAST(var_left.value()), CAST(var_right.value()))); 919 920 BIND(&do_double_mod); 921 Node* value = Float64Mod(var_left_double.value(), var_right_double.value()); 922 Return(AllocateHeapNumberWithValue(value)); 923 924 BIND(&do_bigint_mod); 925 { 926 Node* context = Parameter(Descriptor::kContext); 927 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), 928 var_right.value(), SmiConstant(Operation::kModulus))); 929 } 930 } 931 932 TF_BUILTIN(Exponentiate, NumberBuiltinsAssembler) { 933 VARIABLE(var_left, MachineRepresentation::kTagged); 934 VARIABLE(var_right, MachineRepresentation::kTagged); 935 Label do_number_exp(this), do_bigint_exp(this); 936 Node* context = Parameter(Descriptor::kContext); 937 938 BinaryOp<Descriptor>(&do_number_exp, &var_left, &var_right, &do_number_exp, 939 nullptr, nullptr, &do_bigint_exp); 940 941 BIND(&do_number_exp); 942 { 943 MathBuiltinsAssembler math_asm(state()); 944 Return(math_asm.MathPow(context, var_left.value(), var_right.value())); 945 } 946 947 BIND(&do_bigint_exp); 948 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), 949 var_right.value(), SmiConstant(Operation::kExponentiate))); 950 } 951 952 TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) { 953 EmitBitwiseOp<Descriptor>(Operation::kShiftLeft); 954 } 955 956 TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) { 957 EmitBitwiseOp<Descriptor>(Operation::kShiftRight); 958 } 959 960 TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) { 961 EmitBitwiseOp<Descriptor>(Operation::kShiftRightLogical); 962 } 963 964 TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) { 965 EmitBitwiseOp<Descriptor>(Operation::kBitwiseAnd); 966 } 967 968 TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) { 969 EmitBitwiseOp<Descriptor>(Operation::kBitwiseOr); 970 } 971 972 TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) { 973 EmitBitwiseOp<Descriptor>(Operation::kBitwiseXor); 974 } 975 976 TF_BUILTIN(LessThan, NumberBuiltinsAssembler) { 977 RelationalComparisonBuiltin<Descriptor>(Operation::kLessThan); 978 } 979 980 TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) { 981 RelationalComparisonBuiltin<Descriptor>(Operation::kLessThanOrEqual); 982 } 983 984 TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) { 985 RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThan); 986 } 987 988 TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) { 989 RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThanOrEqual); 990 } 991 992 TF_BUILTIN(Equal, CodeStubAssembler) { 993 Node* lhs = Parameter(Descriptor::kLeft); 994 Node* rhs = Parameter(Descriptor::kRight); 995 Node* context = Parameter(Descriptor::kContext); 996 997 Return(Equal(lhs, rhs, context)); 998 } 999 1000 TF_BUILTIN(StrictEqual, CodeStubAssembler) { 1001 Node* lhs = Parameter(Descriptor::kLeft); 1002 Node* rhs = Parameter(Descriptor::kRight); 1003 1004 Return(StrictEqual(lhs, rhs)); 1005 } 1006 1007 } // namespace internal 1008 } // namespace v8 1009