Home | History | Annotate | Download | only in builtins
      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), &not_number);
    575   if (var_input_double != nullptr) {
    576     var_input_double->Bind(LoadHeapNumberValue(input));
    577   }
    578   Goto(do_double);
    579 
    580   BIND(&not_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)), &dividend_is_zero,
    847            &dividend_is_not_zero);
    848 
    849     BIND(&dividend_is_zero);
    850     {
    851       GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout);
    852       Goto(&dividend_is_not_zero);
    853     }
    854     BIND(&dividend_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