builtins-number-gen.cc 34.3 KB
Newer Older
1 2 3 4
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/builtins/builtins-math-gen.h"
6 7 8
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-stub-assembler.h"
9
#include "src/ic/binary-op-assembler.h"
10 11 12 13 14 15 16 17 18 19 20 21 22

namespace v8 {
namespace internal {

// -----------------------------------------------------------------------------
// ES6 section 20.1 Number Objects

class NumberBuiltinsAssembler : public CodeStubAssembler {
 public:
  explicit NumberBuiltinsAssembler(compiler::CodeAssemblerState* state)
      : CodeStubAssembler(state) {}

 protected:
23
  template <typename Descriptor>
24
  void EmitBitwiseOp(Operation op) {
25 26 27
    Node* left = Parameter(Descriptor::kLeft);
    Node* right = Parameter(Descriptor::kRight);
    Node* context = Parameter(Descriptor::kContext);
28

29 30
    VARIABLE(var_left_word32, MachineRepresentation::kWord32);
    VARIABLE(var_right_word32, MachineRepresentation::kWord32);
31
    VARIABLE(var_left_bigint, MachineRepresentation::kTagged, left);
32 33
    VARIABLE(var_right_bigint, MachineRepresentation::kTagged);
    Label if_left_number(this), do_number_op(this);
34 35
    Label if_left_bigint(this), do_bigint_op(this);

36 37 38 39 40 41 42
    TaggedToWord32OrBigInt(context, left, &if_left_number, &var_left_word32,
                           &if_left_bigint, &var_left_bigint);
    BIND(&if_left_number);
    TaggedToWord32OrBigInt(context, right, &do_number_op, &var_right_word32,
                           &do_bigint_op, &var_right_bigint);
    BIND(&do_number_op);
    Return(BitwiseOp(var_left_word32.value(), var_right_word32.value(), op));
43

44 45
    // BigInt cases.
    BIND(&if_left_bigint);
46
    TaggedToNumeric(context, right, &do_bigint_op, &var_right_bigint);
47 48 49 50 51

    BIND(&do_bigint_op);
    Return(CallRuntime(Runtime::kBigIntBinaryOp, context,
                       var_left_bigint.value(), var_right_bigint.value(),
                       SmiConstant(op)));
52 53
  }

54
  template <typename Descriptor>
55
  void RelationalComparisonBuiltin(Operation op) {
56 57 58
    Node* lhs = Parameter(Descriptor::kLeft);
    Node* rhs = Parameter(Descriptor::kRight);
    Node* context = Parameter(Descriptor::kContext);
59

60
    Return(RelationalComparison(op, lhs, rhs, context));
61
  }
62

63 64 65 66
  template <typename Descriptor>
  void UnaryOp(Variable* var_input, Label* do_smi, Label* do_double,
               Variable* var_input_double, Label* do_bigint);

67 68 69
  template <typename Descriptor>
  void BinaryOp(Label* smis, Variable* var_left, Variable* var_right,
                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 78 79 80 81 82

  Label return_true(this), return_false(this);

  // Check if {number} is a Smi.
  GotoIf(TaggedIsSmi(number), &return_true);

  // Check if {number} is a HeapNumber.
83
  GotoIfNot(IsHeapNumber(number), &return_false);
84 85 86 87 88 89

  // Check if {number} contains a finite, non-NaN value.
  Node* number_value = LoadHeapNumberValue(number);
  BranchIfFloat64IsNaN(Float64Sub(number_value, number_value), &return_false,
                       &return_true);

90
  BIND(&return_true);
91
  Return(TrueConstant());
92

93
  BIND(&return_false);
94
  Return(FalseConstant());
95 96
}

97 98 99 100 101
TF_BUILTIN(AllocateHeapNumber, CodeStubAssembler) {
  Node* result = AllocateHeapNumber();
  Return(result);
}

102
// ES6 #sec-number.isinteger
103
TF_BUILTIN(NumberIsInteger, CodeStubAssembler) {
104 105
  TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
  Return(SelectBooleanConstant(IsInteger(number)));
106 107
}

108
// ES6 #sec-number.isnan
109
TF_BUILTIN(NumberIsNaN, CodeStubAssembler) {
110
  Node* number = Parameter(Descriptor::kNumber);
111 112 113 114 115 116 117

  Label return_true(this), return_false(this);

  // Check if {number} is a Smi.
  GotoIf(TaggedIsSmi(number), &return_false);

  // Check if {number} is a HeapNumber.
118
  GotoIfNot(IsHeapNumber(number), &return_false);
119 120 121 122 123

  // Check if {number} contains a NaN value.
  Node* number_value = LoadHeapNumberValue(number);
  BranchIfFloat64IsNaN(number_value, &return_true, &return_false);

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 134
  TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
  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 143
  VARIABLE(var_input, MachineRepresentation::kTagged,
           Parameter(Descriptor::kString));
144 145
  Label loop(this, &var_input);
  Goto(&loop);
146
  BIND(&loop);
147 148 149 150 151 152 153 154
  {
    // Load the current {input} value.
    Node* input = var_input.value();

    // Check if the {input} is a HeapObject or a Smi.
    Label if_inputissmi(this), if_inputisnotsmi(this);
    Branch(TaggedIsSmi(input), &if_inputissmi, &if_inputisnotsmi);

155
    BIND(&if_inputissmi);
156 157 158 159 160
    {
      // The {input} is already a Number, no need to do anything.
      Return(input);
    }

161
    BIND(&if_inputisnotsmi);
162 163 164 165 166 167 168 169
    {
      // The {input} is a HeapObject, check if it's already a String.
      Label if_inputisstring(this), if_inputisnotstring(this);
      Node* input_map = LoadMap(input);
      Node* input_instance_type = LoadMapInstanceType(input_map);
      Branch(IsStringInstanceType(input_instance_type), &if_inputisstring,
             &if_inputisnotstring);

170
      BIND(&if_inputisstring);
171 172 173 174 175
      {
        // The {input} is already a String, check if {input} contains
        // a cached array index.
        Label if_inputcached(this), if_inputnotcached(this);
        Node* input_hash = LoadNameHashField(input);
176 177 178
        Branch(IsClearWord32(input_hash,
                             Name::kDoesNotContainCachedArrayIndexMask),
               &if_inputcached, &if_inputnotcached);
179

180
        BIND(&if_inputcached);
181 182 183 184 185 186 187
        {
          // Just return the {input}s cached array index.
          Node* input_array_index =
              DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash);
          Return(SmiTag(input_array_index));
        }

188
        BIND(&if_inputnotcached);
189 190 191 192 193 194
        {
          // Need to fall back to the runtime to convert {input} to double.
          Return(CallRuntime(Runtime::kStringParseFloat, context, input));
        }
      }

195
      BIND(&if_inputisnotstring);
196 197 198 199 200 201 202
      {
        // The {input} is neither a String nor a Smi, check for HeapNumber.
        Label if_inputisnumber(this),
            if_inputisnotnumber(this, Label::kDeferred);
        Branch(IsHeapNumberMap(input_map), &if_inputisnumber,
               &if_inputisnotnumber);

203
        BIND(&if_inputisnumber);
204 205 206 207 208 209 210
        {
          // The {input} is already a Number, take care of -0.
          Label if_inputiszero(this), if_inputisnotzero(this);
          Node* input_value = LoadHeapNumberValue(input);
          Branch(Float64Equal(input_value, Float64Constant(0.0)),
                 &if_inputiszero, &if_inputisnotzero);

211
          BIND(&if_inputiszero);
212 213
          Return(SmiConstant(0));

214
          BIND(&if_inputisnotzero);
215 216 217
          Return(input);
        }

218
        BIND(&if_inputisnotnumber);
219 220 221
        {
          // Need to convert the {input} to String first.
          // TODO(bmeurer): This could be more efficient if necessary.
222
          var_input.Bind(CallBuiltin(Builtins::kToString, context, input));
223 224 225 226 227 228 229
          Goto(&loop);
        }
      }
    }
  }
}

230
// ES6 #sec-number.parseint
231
TF_BUILTIN(ParseInt, CodeStubAssembler) {
232 233 234
  Node* context = Parameter(Descriptor::kContext);
  Node* input = Parameter(Descriptor::kString);
  Node* radix = Parameter(Descriptor::kRadix);
235 236 237

  // Check if {radix} is treated as 10 (i.e. undefined, 0 or 10).
  Label if_radix10(this), if_generic(this, Label::kDeferred);
238
  GotoIf(IsUndefined(radix), &if_radix10);
239 240
  GotoIf(WordEqual(radix, SmiConstant(10)), &if_radix10);
  GotoIf(WordEqual(radix, SmiConstant(0)), &if_radix10);
241 242
  Goto(&if_generic);

243
  BIND(&if_radix10);
244 245 246 247 248 249 250 251 252 253 254
  {
    // Check if we can avoid the ToString conversion on {input}.
    Label if_inputissmi(this), if_inputisheapnumber(this),
        if_inputisstring(this);
    GotoIf(TaggedIsSmi(input), &if_inputissmi);
    Node* input_map = LoadMap(input);
    GotoIf(IsHeapNumberMap(input_map), &if_inputisheapnumber);
    Node* input_instance_type = LoadMapInstanceType(input_map);
    Branch(IsStringInstanceType(input_instance_type), &if_inputisstring,
           &if_generic);

255
    BIND(&if_inputissmi);
256 257 258 259 260
    {
      // Just return the {input}.
      Return(input);
    }

261
    BIND(&if_inputisheapnumber);
262 263 264 265 266 267 268 269
    {
      // Check if the {input} value is in Signed32 range.
      Label if_inputissigned32(this);
      Node* input_value = LoadHeapNumberValue(input);
      Node* input_value32 = TruncateFloat64ToWord32(input_value);
      GotoIf(Float64Equal(input_value, ChangeInt32ToFloat64(input_value32)),
             &if_inputissigned32);

270 271 272
      // Check if the absolute {input} value is in the [1,1<<31[ range.
      // Take the generic path for the range [0,1[ because the result
      // could be -0.
273 274
      Node* input_value_abs = Float64Abs(input_value);

275
      GotoIfNot(Float64LessThan(input_value_abs, Float64Constant(1u << 31)),
276
                &if_generic);
277
      Branch(Float64LessThanOrEqual(Float64Constant(1), input_value_abs),
278 279 280
             &if_inputissigned32, &if_generic);

      // Return the truncated int32 value, and return the tagged result.
281
      BIND(&if_inputissigned32);
282 283 284 285
      Node* result = ChangeInt32ToTagged(input_value32);
      Return(result);
    }

286
    BIND(&if_inputisstring);
287 288 289
    {
      // Check if the String {input} has a cached array index.
      Node* input_hash = LoadNameHashField(input);
290 291
      GotoIf(IsSetWord32(input_hash, Name::kDoesNotContainCachedArrayIndexMask),
             &if_generic);
292 293 294 295 296 297 298 299 300

      // Return the cached array index as result.
      Node* input_index =
          DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash);
      Node* result = SmiTag(input_index);
      Return(result);
    }
  }

301
  BIND(&if_generic);
302 303 304 305 306 307
  {
    Node* result = CallRuntime(Runtime::kStringParseInt, context, input, radix);
    Return(result);
  }
}

308 309 310 311 312 313 314 315
// ES6 #sec-number.parseint
TF_BUILTIN(NumberParseInt, CodeStubAssembler) {
  Node* context = Parameter(Descriptor::kContext);
  Node* input = Parameter(Descriptor::kString);
  Node* radix = Parameter(Descriptor::kRadix);
  Return(CallBuiltin(Builtins::kParseInt, context, input, radix));
}

316
// ES6 #sec-number.prototype.valueof
317
TF_BUILTIN(NumberPrototypeValueOf, CodeStubAssembler) {
318 319
  Node* context = Parameter(Descriptor::kContext);
  Node* receiver = Parameter(Descriptor::kReceiver);
320 321 322 323 324 325

  Node* result = ToThisValue(context, receiver, PrimitiveType::kNumber,
                             "Number.prototype.valueOf");
  Return(result);
}

326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
class AddStubAssembler : public CodeStubAssembler {
 public:
  explicit AddStubAssembler(compiler::CodeAssemblerState* state)
      : CodeStubAssembler(state) {}

 protected:
  void ConvertReceiverAndLoop(Variable* var_value, Label* loop, Node* context) {
    // Call ToPrimitive explicitly without hint (whereas ToNumber
    // would pass a "number" hint).
    Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate());
    var_value->Bind(CallStub(callable, context, var_value->value()));
    Goto(loop);
  }

  void ConvertNonReceiverAndLoop(Variable* var_value, Label* loop,
                                 Node* context) {
342 343
    var_value->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
                                var_value->value()));
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
    Goto(loop);
  }

  void ConvertAndLoop(Variable* var_value, Node* instance_type, Label* loop,
                      Node* context) {
    Label is_not_receiver(this, Label::kDeferred);
    GotoIfNot(IsJSReceiverInstanceType(instance_type), &is_not_receiver);

    ConvertReceiverAndLoop(var_value, loop, context);

    BIND(&is_not_receiver);
    ConvertNonReceiverAndLoop(var_value, loop, context);
  }
};

TF_BUILTIN(Add, AddStubAssembler) {
360
  Node* context = Parameter(Descriptor::kContext);
361 362 363 364
  VARIABLE(var_left, MachineRepresentation::kTagged,
           Parameter(Descriptor::kLeft));
  VARIABLE(var_right, MachineRepresentation::kTagged,
           Parameter(Descriptor::kRight));
365 366

  // Shared entry for floating point addition.
367 368 369
  Label do_double_add(this);
  VARIABLE(var_left_double, MachineRepresentation::kFloat64);
  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 375
  Variable* loop_vars[2] = {&var_left, &var_right};
  Label loop(this, 2, loop_vars),
376
      string_add_convert_left(this, Label::kDeferred),
377 378
      string_add_convert_right(this, Label::kDeferred),
      do_bigint_add(this, Label::kDeferred);
379
  Goto(&loop);
380
  BIND(&loop);
381
  {
382 383
    Node* left = var_left.value();
    Node* right = var_right.value();
384

385 386
    Label if_left_smi(this), if_left_heapobject(this);
    Branch(TaggedIsSmi(left), &if_left_smi, &if_left_heapobject);
387

388
    BIND(&if_left_smi);
389
    {
390 391
      Label if_right_smi(this), if_right_heapobject(this);
      Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject);
392

393
      BIND(&if_right_smi);
394
      {
395
        Label if_overflow(this);
396 397
        TNode<Smi> result = TrySmiAdd(CAST(left), CAST(right), &if_overflow);
        Return(result);
398

399
        BIND(&if_overflow);
400
        {
401 402 403
          var_left_double.Bind(SmiToFloat64(left));
          var_right_double.Bind(SmiToFloat64(right));
          Goto(&do_double_add);
404
        }
405
      }  // if_right_smi
406

407
      BIND(&if_right_heapobject);
408
      {
409
        Node* right_map = LoadMap(right);
410

411 412
        Label if_right_not_number(this, Label::kDeferred);
        GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number);
413

414 415 416 417
        // {right} is a HeapNumber.
        var_left_double.Bind(SmiToFloat64(left));
        var_right_double.Bind(LoadHeapNumberValue(right));
        Goto(&do_double_add);
418

419
        BIND(&if_right_not_number);
420
        {
421 422 423
          Node* right_instance_type = LoadMapInstanceType(right_map);
          GotoIf(IsStringInstanceType(right_instance_type),
                 &string_add_convert_left);
424
          GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add);
425
          ConvertAndLoop(&var_right, right_instance_type, &loop, context);
426
        }
427 428
      }  // if_right_heapobject
    }    // if_left_smi
429

430
    BIND(&if_left_heapobject);
431
    {
432 433 434
      Node* left_map = LoadMap(left);
      Label if_right_smi(this), if_right_heapobject(this);
      Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject);
435

436
      BIND(&if_right_smi);
437
      {
438 439
        Label if_left_not_number(this, Label::kDeferred);
        GotoIfNot(IsHeapNumberMap(left_map), &if_left_not_number);
440

441 442 443 444
        // {left} is a HeapNumber, {right} is a Smi.
        var_left_double.Bind(LoadHeapNumberValue(left));
        var_right_double.Bind(SmiToFloat64(right));
        Goto(&do_double_add);
445

446
        BIND(&if_left_not_number);
447
        {
448 449 450
          Node* left_instance_type = LoadMapInstanceType(left_map);
          GotoIf(IsStringInstanceType(left_instance_type),
                 &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 454 455
          ConvertAndLoop(&var_left, left_instance_type, &loop, context);
        }
      }  // if_right_smi
456

457 458 459
      BIND(&if_right_heapobject);
      {
        Node* right_map = LoadMap(right);
460

461 462
        Label if_left_number(this), if_left_not_number(this, Label::kDeferred);
        Branch(IsHeapNumberMap(left_map), &if_left_number, &if_left_not_number);
463

464
        BIND(&if_left_number);
465
        {
466 467
          Label if_right_not_number(this, Label::kDeferred);
          GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number);
468

469 470 471 472
          // Both {left} and {right} are HeapNumbers.
          var_left_double.Bind(LoadHeapNumberValue(left));
          var_right_double.Bind(LoadHeapNumberValue(right));
          Goto(&do_double_add);
473

474
          BIND(&if_right_not_number);
475
          {
476 477 478
            Node* right_instance_type = LoadMapInstanceType(right_map);
            GotoIf(IsStringInstanceType(right_instance_type),
                   &string_add_convert_left);
479
            GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add);
480 481
            // {left} is a HeapNumber, {right} is neither Number nor String.
            ConvertAndLoop(&var_right, right_instance_type, &loop, context);
482
          }
483
        }  // if_left_number
484

485 486
        BIND(&if_left_not_number);
        {
487
          Label if_left_bigint(this);
488 489 490 491 492 493
          Node* left_instance_type = LoadMapInstanceType(left_map);
          GotoIf(IsStringInstanceType(left_instance_type),
                 &string_add_convert_right);
          Node* right_instance_type = LoadMapInstanceType(right_map);
          GotoIf(IsStringInstanceType(right_instance_type),
                 &string_add_convert_left);
494
          GotoIf(IsBigIntInstanceType(left_instance_type), &if_left_bigint);
495 496 497 498 499 500 501
          Label if_left_not_receiver(this, Label::kDeferred);
          Label if_right_not_receiver(this, Label::kDeferred);
          GotoIfNot(IsJSReceiverInstanceType(left_instance_type),
                    &if_left_not_receiver);
          // {left} is a JSReceiver, convert it first.
          ConvertReceiverAndLoop(&var_left, &loop, context);

502 503 504 505 506 507 508 509 510
          BIND(&if_left_bigint);
          {
            // {right} is a HeapObject, but not a String. Jump to
            // {do_bigint_add} if {right} is already a Numeric.
            GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add);
            GotoIf(IsHeapNumberMap(right_map), &do_bigint_add);
            ConvertAndLoop(&var_right, right_instance_type, &loop, context);
          }

511 512 513 514 515 516 517 518 519 520
          BIND(&if_left_not_receiver);
          GotoIfNot(IsJSReceiverInstanceType(right_instance_type),
                    &if_right_not_receiver);
          // {left} is a Primitive, but {right} is a JSReceiver, so convert
          // {right} with priority.
          ConvertReceiverAndLoop(&var_right, &loop, context);

          BIND(&if_right_not_receiver);
          // Neither {left} nor {right} are JSReceivers.
          ConvertNonReceiverAndLoop(&var_left, &loop, context);
521
        }
522 523
      }  // if_right_heapobject
    }    // 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 529
    TailCallBuiltin(Builtins::kStringAdd_ConvertLeft, context, var_left.value(),
                    var_right.value());
530 531
  }

532
  BIND(&string_add_convert_right);
533
  {
534
    // Convert {right} to a String and concatenate it with the String {left}.
535 536
    TailCallBuiltin(Builtins::kStringAdd_ConvertRight, context,
                    var_left.value(), var_right.value());
537 538
  }

539 540 541
  BIND(&do_bigint_add);
  {
    Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
542
                       var_right.value(), SmiConstant(Operation::kAdd)));
543 544
  }

545
  BIND(&do_double_add);
546
  {
547 548
    Node* value = Float64Add(var_left_double.value(), var_right_double.value());
    Return(AllocateHeapNumberWithValue(value));
549 550 551
  }
}

552 553 554 555 556 557
template <typename Descriptor>
void NumberBuiltinsAssembler::UnaryOp(Variable* var_input, Label* do_smi,
                                      Label* do_double,
                                      Variable* var_input_double,
                                      Label* do_bigint) {
  DCHECK_EQ(var_input->rep(), MachineRepresentation::kTagged);
558 559
  DCHECK_IMPLIES(var_input_double != nullptr,
                 var_input_double->rep() == MachineRepresentation::kFloat64);
560 561 562 563 564 565 566 567 568 569 570 571 572

  Node* context = Parameter(Descriptor::kContext);
  var_input->Bind(Parameter(Descriptor::kValue));

  // We might need to loop for ToNumeric conversion.
  Label loop(this, {var_input});
  Goto(&loop);
  BIND(&loop);
  Node* input = var_input->value();

  Label not_number(this);
  GotoIf(TaggedIsSmi(input), do_smi);
  GotoIfNot(IsHeapNumber(input), &not_number);
573 574 575
  if (var_input_double != nullptr) {
    var_input_double->Bind(LoadHeapNumberValue(input));
  }
576 577 578 579 580 581 582 583
  Goto(do_double);

  BIND(&not_number);
  GotoIf(IsBigInt(input), do_bigint);
  var_input->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context, input));
  Goto(&loop);
}

584 585 586 587
template <typename Descriptor>
void NumberBuiltinsAssembler::BinaryOp(Label* smis, Variable* var_left,
                                       Variable* var_right, Label* doubles,
                                       Variable* var_left_double,
588 589
                                       Variable* var_right_double,
                                       Label* bigints) {
590 591
  DCHECK_EQ(var_left->rep(), MachineRepresentation::kTagged);
  DCHECK_EQ(var_right->rep(), MachineRepresentation::kTagged);
592 593 594 595 596
  DCHECK_IMPLIES(var_left_double != nullptr,
                 var_left_double->rep() == MachineRepresentation::kFloat64);
  DCHECK_IMPLIES(var_right_double != nullptr,
                 var_right_double->rep() == MachineRepresentation::kFloat64);
  DCHECK_EQ(var_left_double == nullptr, var_right_double == nullptr);
597

598
  Node* context = Parameter(Descriptor::kContext);
599 600 601
  var_left->Bind(Parameter(Descriptor::kLeft));
  var_right->Bind(Parameter(Descriptor::kRight));

602
  // We might need to loop for ToNumeric conversions.
603
  Label loop(this, {var_left, var_right});
604
  Goto(&loop);
605
  BIND(&loop);
606

607 608 609 610
  Label left_not_smi(this), right_not_smi(this);
  Label left_not_number(this), right_not_number(this);
  GotoIfNot(TaggedIsSmi(var_left->value()), &left_not_smi);
  GotoIf(TaggedIsSmi(var_right->value()), smis);
611

612 613
  // At this point, var_left is a Smi but var_right is not.
  GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number);
614 615 616 617
  if (var_left_double != nullptr) {
    var_left_double->Bind(SmiToFloat64(var_left->value()));
    var_right_double->Bind(LoadHeapNumberValue(var_right->value()));
  }
618
  Goto(doubles);
619

620 621 622 623
  BIND(&left_not_smi);
  {
    GotoIfNot(IsHeapNumber(var_left->value()), &left_not_number);
    GotoIfNot(TaggedIsSmi(var_right->value()), &right_not_smi);
624

625
    // At this point, var_left is a HeapNumber and var_right is a Smi.
626 627 628 629
    if (var_left_double != nullptr) {
      var_left_double->Bind(LoadHeapNumberValue(var_left->value()));
      var_right_double->Bind(SmiToFloat64(var_right->value()));
    }
630 631
    Goto(doubles);
  }
632

633 634 635
  BIND(&right_not_smi);
  {
    GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number);
636 637 638 639
    if (var_left_double != nullptr) {
      var_left_double->Bind(LoadHeapNumberValue(var_left->value()));
      var_right_double->Bind(LoadHeapNumberValue(var_right->value()));
    }
640 641
    Goto(doubles);
  }
642

643 644
  BIND(&left_not_number);
  {
645 646
    Label left_bigint(this);
    GotoIf(IsBigInt(var_left->value()), &left_bigint);
647
    var_left->Bind(
648
        CallBuiltin(Builtins::kNonNumberToNumeric, context, var_left->value()));
649
    Goto(&loop);
650 651 652 653 654 655 656 657 658 659 660

    BIND(&left_bigint);
    {
      // Jump to {bigints} if {var_right} is already a Numeric.
      GotoIf(TaggedIsSmi(var_right->value()), bigints);
      GotoIf(IsBigInt(var_right->value()), bigints);
      GotoIf(IsHeapNumber(var_right->value()), bigints);
      var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
                                  var_right->value()));
      Goto(&loop);
    }
661 662
  }

663
  BIND(&right_not_number);
664
  {
665
    GotoIf(IsBigInt(var_right->value()), bigints);
666 667
    var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
                                var_right->value()));
668
    Goto(&loop);
669 670 671
  }
}

672 673 674 675 676
TF_BUILTIN(Subtract, NumberBuiltinsAssembler) {
  VARIABLE(var_left, MachineRepresentation::kTagged);
  VARIABLE(var_right, MachineRepresentation::kTagged);
  VARIABLE(var_left_double, MachineRepresentation::kFloat64);
  VARIABLE(var_right_double, MachineRepresentation::kFloat64);
677
  Label do_smi_sub(this), do_double_sub(this), do_bigint_sub(this);
678

679
  BinaryOp<Descriptor>(&do_smi_sub, &var_left, &var_right, &do_double_sub,
680
                       &var_left_double, &var_right_double, &do_bigint_sub);
681

682
  BIND(&do_smi_sub);
683
  {
684 685 686 687
    Label if_overflow(this);
    TNode<Smi> result = TrySmiSub(CAST(var_left.value()),
                                  CAST(var_right.value()), &if_overflow);
    Return(result);
688 689

    BIND(&if_overflow);
690
    {
691 692 693
      var_left_double.Bind(SmiToFloat64(var_left.value()));
      var_right_double.Bind(SmiToFloat64(var_right.value()));
      Goto(&do_double_sub);
694 695 696
    }
  }

697
  BIND(&do_double_sub);
698
  {
699 700
    Node* value = Float64Sub(var_left_double.value(), var_right_double.value());
    Return(AllocateHeapNumberWithValue(value));
701
  }
702 703 704 705 706

  BIND(&do_bigint_sub);
  {
    Node* context = Parameter(Descriptor::kContext);
    Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
707
                       var_right.value(), SmiConstant(Operation::kSubtract)));
708
  }
709 710
}

711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
TF_BUILTIN(BitwiseNot, NumberBuiltinsAssembler) {
  Node* context = Parameter(Descriptor::kContext);
  VARIABLE(var_input, MachineRepresentation::kTagged);
  Label do_number(this), do_bigint(this);

  UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);

  BIND(&do_number);
  {
    TailCallBuiltin(Builtins::kBitwiseXor, context, var_input.value(),
                    SmiConstant(-1));
  }

  BIND(&do_bigint);
  {
    Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
                       SmiConstant(Operation::kBitwiseNot)));
  }
}

731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
TF_BUILTIN(Decrement, NumberBuiltinsAssembler) {
  Node* context = Parameter(Descriptor::kContext);
  VARIABLE(var_input, MachineRepresentation::kTagged);
  Label do_number(this), do_bigint(this);

  UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);

  BIND(&do_number);
  {
    TailCallBuiltin(Builtins::kSubtract, context, var_input.value(),
                    SmiConstant(1));
  }

  BIND(&do_bigint);
  {
    Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
                       SmiConstant(Operation::kDecrement)));
  }
}

TF_BUILTIN(Increment, NumberBuiltinsAssembler) {
  Node* context = Parameter(Descriptor::kContext);
  VARIABLE(var_input, MachineRepresentation::kTagged);
  Label do_number(this), do_bigint(this);

  UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);

  BIND(&do_number);
  {
    TailCallBuiltin(Builtins::kAdd, context, var_input.value(), SmiConstant(1));
  }

  BIND(&do_bigint);
  {
    Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
                       SmiConstant(Operation::kIncrement)));
  }
}

770 771 772 773 774 775 776 777 778
TF_BUILTIN(Negate, NumberBuiltinsAssembler) {
  VARIABLE(var_input, MachineRepresentation::kTagged);
  VARIABLE(var_input_double, MachineRepresentation::kFloat64);
  Label do_smi(this), do_double(this), do_bigint(this);

  UnaryOp<Descriptor>(&var_input, &do_smi, &do_double, &var_input_double,
                      &do_bigint);

  BIND(&do_smi);
779
  { Return(SmiMul(CAST(var_input.value()), SmiConstant(-1))); }
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794

  BIND(&do_double);
  {
    Node* value = Float64Mul(var_input_double.value(), Float64Constant(-1));
    Return(AllocateHeapNumberWithValue(value));
  }

  BIND(&do_bigint);
  {
    Node* context = Parameter(Descriptor::kContext);
    Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
                       SmiConstant(Operation::kNegate)));
  }
}

795 796 797 798 799
TF_BUILTIN(Multiply, NumberBuiltinsAssembler) {
  VARIABLE(var_left, MachineRepresentation::kTagged);
  VARIABLE(var_right, MachineRepresentation::kTagged);
  VARIABLE(var_left_double, MachineRepresentation::kFloat64);
  VARIABLE(var_right_double, MachineRepresentation::kFloat64);
800
  Label do_smi_mul(this), do_double_mul(this), do_bigint_mul(this);
801

802
  BinaryOp<Descriptor>(&do_smi_mul, &var_left, &var_right, &do_double_mul,
803
                       &var_left_double, &var_right_double, &do_bigint_mul);
804

805 806
  BIND(&do_smi_mul);
  // The result is not necessarily a smi, in case of overflow.
807
  Return(SmiMul(CAST(var_left.value()), CAST(var_right.value())));
808

809 810 811
  BIND(&do_double_mul);
  Node* value = Float64Mul(var_left_double.value(), var_right_double.value());
  Return(AllocateHeapNumberWithValue(value));
812 813 814 815 816

  BIND(&do_bigint_mul);
  {
    Node* context = Parameter(Descriptor::kContext);
    Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
817
                       var_right.value(), SmiConstant(Operation::kMultiply)));
818
  }
819
}
820

821 822 823 824 825
TF_BUILTIN(Divide, NumberBuiltinsAssembler) {
  VARIABLE(var_left, MachineRepresentation::kTagged);
  VARIABLE(var_right, MachineRepresentation::kTagged);
  VARIABLE(var_left_double, MachineRepresentation::kFloat64);
  VARIABLE(var_right_double, MachineRepresentation::kFloat64);
826
  Label do_smi_div(this), do_double_div(this), do_bigint_div(this);
827

828
  BinaryOp<Descriptor>(&do_smi_div, &var_left, &var_right, &do_double_div,
829
                       &var_left_double, &var_right_double, &do_bigint_div);
830

831 832 833 834
  BIND(&do_smi_div);
  {
    // TODO(jkummerow): Consider just always doing a double division.
    Label bailout(this);
835 836
    TNode<Smi> dividend = CAST(var_left.value());
    TNode<Smi> divisor = CAST(var_right.value());
837

838 839
    // Do floating point division if {divisor} is zero.
    GotoIf(SmiEqual(divisor, SmiConstant(0)), &bailout);
840

841 842 843 844 845
    // Do floating point division if {dividend} is zero and {divisor} is
    // negative.
    Label dividend_is_zero(this), dividend_is_not_zero(this);
    Branch(SmiEqual(dividend, SmiConstant(0)), &dividend_is_zero,
           &dividend_is_not_zero);
846

847 848 849 850 851 852
    BIND(&dividend_is_zero);
    {
      GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout);
      Goto(&dividend_is_not_zero);
    }
    BIND(&dividend_is_not_zero);
853

854 855
    Node* untagged_divisor = SmiToInt32(divisor);
    Node* untagged_dividend = SmiToInt32(dividend);
856

857 858 859 860 861
    // Do floating point division if {dividend} is kMinInt (or kMinInt - 1
    // if the Smi size is 31) and {divisor} is -1.
    Label divisor_is_minus_one(this), divisor_is_not_minus_one(this);
    Branch(Word32Equal(untagged_divisor, Int32Constant(-1)),
           &divisor_is_minus_one, &divisor_is_not_minus_one);
862

863 864 865 866 867 868 869
    BIND(&divisor_is_minus_one);
    {
      GotoIf(Word32Equal(
                 untagged_dividend,
                 Int32Constant(kSmiValueSize == 32 ? kMinInt : (kMinInt >> 1))),
             &bailout);
      Goto(&divisor_is_not_minus_one);
870
    }
871 872 873 874 875 876 877 878
    BIND(&divisor_is_not_minus_one);

    // TODO(epertoso): consider adding a machine instruction that returns
    // both the result and the remainder.
    Node* untagged_result = Int32Div(untagged_dividend, untagged_divisor);
    Node* truncated = Int32Mul(untagged_result, untagged_divisor);
    // Do floating point division if the remainder is not 0.
    GotoIf(Word32NotEqual(untagged_dividend, truncated), &bailout);
879
    Return(SmiFromInt32(untagged_result));
880 881 882 883

    // Bailout: convert {dividend} and {divisor} to double and do double
    // division.
    BIND(&bailout);
884
    {
885 886 887
      var_left_double.Bind(SmiToFloat64(dividend));
      var_right_double.Bind(SmiToFloat64(divisor));
      Goto(&do_double_div);
888 889 890
    }
  }

891
  BIND(&do_double_div);
892
  {
893 894
    Node* value = Float64Div(var_left_double.value(), var_right_double.value());
    Return(AllocateHeapNumberWithValue(value));
895
  }
896 897 898 899 900

  BIND(&do_bigint_div);
  {
    Node* context = Parameter(Descriptor::kContext);
    Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
901
                       var_right.value(), SmiConstant(Operation::kDivide)));
902
  }
903 904
}

905 906 907 908 909
TF_BUILTIN(Modulus, NumberBuiltinsAssembler) {
  VARIABLE(var_left, MachineRepresentation::kTagged);
  VARIABLE(var_right, MachineRepresentation::kTagged);
  VARIABLE(var_left_double, MachineRepresentation::kFloat64);
  VARIABLE(var_right_double, MachineRepresentation::kFloat64);
910
  Label do_smi_mod(this), do_double_mod(this), do_bigint_mod(this);
911

912
  BinaryOp<Descriptor>(&do_smi_mod, &var_left, &var_right, &do_double_mod,
913
                       &var_left_double, &var_right_double, &do_bigint_mod);
914

915
  BIND(&do_smi_mod);
916
  Return(SmiMod(CAST(var_left.value()), CAST(var_right.value())));
917

918 919 920
  BIND(&do_double_mod);
  Node* value = Float64Mod(var_left_double.value(), var_right_double.value());
  Return(AllocateHeapNumberWithValue(value));
921 922 923 924 925

  BIND(&do_bigint_mod);
  {
    Node* context = Parameter(Descriptor::kContext);
    Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
926
                       var_right.value(), SmiConstant(Operation::kModulus)));
927
  }
928 929
}

930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
TF_BUILTIN(Exponentiate, NumberBuiltinsAssembler) {
  VARIABLE(var_left, MachineRepresentation::kTagged);
  VARIABLE(var_right, MachineRepresentation::kTagged);
  Label do_number_exp(this), do_bigint_exp(this);
  Node* context = Parameter(Descriptor::kContext);

  BinaryOp<Descriptor>(&do_number_exp, &var_left, &var_right, &do_number_exp,
                       nullptr, nullptr, &do_bigint_exp);

  BIND(&do_number_exp);
  {
    MathBuiltinsAssembler math_asm(state());
    Return(math_asm.MathPow(context, var_left.value(), var_right.value()));
  }

  BIND(&do_bigint_exp);
  Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
                     var_right.value(), SmiConstant(Operation::kExponentiate)));
}

950
TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) {
951
  EmitBitwiseOp<Descriptor>(Operation::kShiftLeft);
952 953 954
}

TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) {
955
  EmitBitwiseOp<Descriptor>(Operation::kShiftRight);
956 957 958
}

TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) {
959
  EmitBitwiseOp<Descriptor>(Operation::kShiftRightLogical);
960 961 962
}

TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) {
963
  EmitBitwiseOp<Descriptor>(Operation::kBitwiseAnd);
964 965 966
}

TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) {
967
  EmitBitwiseOp<Descriptor>(Operation::kBitwiseOr);
968 969 970
}

TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) {
971
  EmitBitwiseOp<Descriptor>(Operation::kBitwiseXor);
972 973 974
}

TF_BUILTIN(LessThan, NumberBuiltinsAssembler) {
975
  RelationalComparisonBuiltin<Descriptor>(Operation::kLessThan);
976 977 978
}

TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) {
979
  RelationalComparisonBuiltin<Descriptor>(Operation::kLessThanOrEqual);
980 981 982
}

TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) {
983
  RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThan);
984 985 986
}

TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) {
987
  RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThanOrEqual);
988 989 990
}

TF_BUILTIN(Equal, CodeStubAssembler) {
991 992 993
  Node* lhs = Parameter(Descriptor::kLeft);
  Node* rhs = Parameter(Descriptor::kRight);
  Node* context = Parameter(Descriptor::kContext);
994 995 996 997 998

  Return(Equal(lhs, rhs, context));
}

TF_BUILTIN(StrictEqual, CodeStubAssembler) {
999 1000
  Node* lhs = Parameter(Descriptor::kLeft);
  Node* rhs = Parameter(Descriptor::kRight);
1001 1002 1003 1004 1005 1006

  Return(StrictEqual(lhs, rhs));
}

}  // namespace internal
}  // namespace v8