binary-op-assembler.cc 17.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Copyright 2016 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.

#include "src/ic/binary-op-assembler.h"

#include "src/globals.h"

namespace v8 {
namespace internal {

using compiler::Node;

Node* BinaryOpAssembler::Generate_AddWithFeedback(Node* context, Node* lhs,
                                                  Node* rhs, Node* slot_id,
16
                                                  Node* feedback_vector,
17
                                                  bool rhs_is_smi) {
18 19 20 21
  // Shared entry for floating point addition.
  Label do_fadd(this), if_lhsisnotnumber(this, Label::kDeferred),
      check_rhsisoddball(this, Label::kDeferred),
      call_with_oddball_feedback(this), call_with_any_feedback(this),
22
      call_add_stub(this), end(this), bigint(this, Label::kDeferred);
23 24 25 26
  VARIABLE(var_fadd_lhs, MachineRepresentation::kFloat64);
  VARIABLE(var_fadd_rhs, MachineRepresentation::kFloat64);
  VARIABLE(var_type_feedback, MachineRepresentation::kTaggedSigned);
  VARIABLE(var_result, MachineRepresentation::kTagged);
27 28

  // Check if the {lhs} is a Smi or a HeapObject.
29 30 31 32 33 34 35 36
  Label if_lhsissmi(this);
  // If rhs is known to be an Smi we want to fast path Smi operation. This is
  // for AddSmi operation. For the normal Add operation, we want to fast path
  // both Smi and Number operations, so this path should not be marked as
  // Deferred.
  Label if_lhsisnotsmi(this,
                       rhs_is_smi ? Label::kDeferred : Label::kNonDeferred);
  Branch(TaggedIsNotSmi(lhs), &if_lhsisnotsmi, &if_lhsissmi);
37

38
  BIND(&if_lhsissmi);
39
  {
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    Comment("lhs is Smi");
    if (!rhs_is_smi) {
      // Check if the {rhs} is also a Smi.
      Label if_rhsissmi(this), if_rhsisnotsmi(this);
      Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);

      BIND(&if_rhsisnotsmi);
      {
        // Check if the {rhs} is a HeapNumber.
        GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);

        var_fadd_lhs.Bind(SmiToFloat64(lhs));
        var_fadd_rhs.Bind(LoadHeapNumberValue(rhs));
        Goto(&do_fadd);
      }

      BIND(&if_rhsissmi);
    }
58 59

    {
60 61 62 63 64 65
      Comment("perform smi operation");
      // If rhs is known to be an Smi we want to fast path Smi operation. This
      // is for AddSmi operation. For the normal Add operation, we want to fast
      // path both Smi and Number operations, so this path should not be marked
      // as Deferred.
      Label if_overflow(this,
66 67 68 69 70 71 72 73 74
                        rhs_is_smi ? Label::kDeferred : Label::kNonDeferred);
      TNode<Smi> smi_result = TrySmiAdd(CAST(lhs), CAST(rhs), &if_overflow);
      // Not overflowed.
      {
        var_type_feedback.Bind(
            SmiConstant(BinaryOperationFeedback::kSignedSmall));
        var_result.Bind(smi_result);
        Goto(&end);
      }
75

76
      BIND(&if_overflow);
77 78 79 80 81 82 83 84
      {
        var_fadd_lhs.Bind(SmiToFloat64(lhs));
        var_fadd_rhs.Bind(SmiToFloat64(rhs));
        Goto(&do_fadd);
      }
    }
  }

85
  BIND(&if_lhsisnotsmi);
86 87
  {
    // Check if {lhs} is a HeapNumber.
88
    GotoIfNot(IsHeapNumber(lhs), &if_lhsisnotnumber);
89

90 91 92 93
    if (!rhs_is_smi) {
      // Check if the {rhs} is Smi.
      Label if_rhsissmi(this), if_rhsisnotsmi(this);
      Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
94

95 96 97 98
      BIND(&if_rhsisnotsmi);
      {
        // Check if the {rhs} is a HeapNumber.
        GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);
99

100 101 102 103
        var_fadd_lhs.Bind(LoadHeapNumberValue(lhs));
        var_fadd_rhs.Bind(LoadHeapNumberValue(rhs));
        Goto(&do_fadd);
      }
104

105 106 107
      BIND(&if_rhsissmi);
    }
    {
108
      var_fadd_lhs.Bind(LoadHeapNumberValue(lhs));
109
      var_fadd_rhs.Bind(SmiToFloat64(rhs));
110 111 112 113
      Goto(&do_fadd);
    }
  }

114
  BIND(&do_fadd);
115
  {
116
    var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNumber));
117 118 119 120 121 122
    Node* value = Float64Add(var_fadd_lhs.value(), var_fadd_rhs.value());
    Node* result = AllocateHeapNumberWithValue(value);
    var_result.Bind(result);
    Goto(&end);
  }

123
  BIND(&if_lhsisnotnumber);
124 125 126 127
  {
    // No checks on rhs are done yet. We just know lhs is not a number or Smi.
    Label if_lhsisoddball(this), if_lhsisnotoddball(this);
    Node* lhs_instance_type = LoadInstanceType(lhs);
128
    Node* lhs_is_oddball = InstanceTypeEqual(lhs_instance_type, ODDBALL_TYPE);
129 130
    Branch(lhs_is_oddball, &if_lhsisoddball, &if_lhsisnotoddball);

131
    BIND(&if_lhsisoddball);
132 133
    {
      GotoIf(TaggedIsSmi(rhs), &call_with_oddball_feedback);
134

135
      // Check if {rhs} is a HeapNumber.
136
      Branch(IsHeapNumber(rhs), &call_with_oddball_feedback,
137 138 139
             &check_rhsisoddball);
    }

140
    BIND(&if_lhsisnotoddball);
141
    {
142 143 144 145 146 147 148
      Label lhs_is_string(this), lhs_is_bigint(this);
      GotoIf(IsStringInstanceType(lhs_instance_type), &lhs_is_string);
      GotoIf(IsBigIntInstanceType(lhs_instance_type), &lhs_is_bigint);
      Goto(&call_with_any_feedback);

      BIND(&lhs_is_bigint);
      {
149 150
        GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);
        Branch(IsBigInt(rhs), &bigint, &call_with_any_feedback);
151
      }
152

153
      BIND(&lhs_is_string);
154 155 156 157 158 159 160 161 162 163 164
      // Check if the {rhs} is a smi, and exit the string check early if it is.
      GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);

      Node* rhs_instance_type = LoadInstanceType(rhs);

      // Exit unless {rhs} is a string. Since {lhs} is a string we no longer
      // need an Oddball check.
      GotoIfNot(IsStringInstanceType(rhs_instance_type),
                &call_with_any_feedback);

      var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kString));
165 166
      var_result.Bind(
          CallBuiltin(Builtins::kStringAdd_CheckNone, context, lhs, rhs));
167 168 169 170 171

      Goto(&end);
    }
  }

172
  BIND(&check_rhsisoddball);
173 174 175 176
  {
    // Check if rhs is an oddball. At this point we know lhs is either a
    // Smi or number or oddball and rhs is not a number or Smi.
    Node* rhs_instance_type = LoadInstanceType(rhs);
177
    Node* rhs_is_oddball = InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE);
178 179
    GotoIf(rhs_is_oddball, &call_with_oddball_feedback);
    Branch(IsBigIntInstanceType(rhs_instance_type), &bigint,
180 181 182
           &call_with_any_feedback);
  }

183 184 185 186
  BIND(&bigint);
  {
    var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kBigInt));
    var_result.Bind(CallRuntime(Runtime::kBigIntBinaryOp, context, lhs, rhs,
187
                                SmiConstant(Operation::kAdd)));
188 189 190
    Goto(&end);
  }

191
  BIND(&call_with_oddball_feedback);
192 193 194 195 196 197
  {
    var_type_feedback.Bind(
        SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
    Goto(&call_add_stub);
  }

198
  BIND(&call_with_any_feedback);
199 200 201 202 203
  {
    var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny));
    Goto(&call_add_stub);
  }

204
  BIND(&call_add_stub);
205
  {
206
    var_result.Bind(CallBuiltin(Builtins::kAdd, context, lhs, rhs));
207 208 209
    Goto(&end);
  }

210
  BIND(&end);
211
  UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_id);
212 213 214
  return var_result.value();
}

215 216
Node* BinaryOpAssembler::Generate_BinaryOperationWithFeedback(
    Node* context, Node* lhs, Node* rhs, Node* slot_id, Node* feedback_vector,
217
    const SmiOperation& smiOperation, const FloatOperation& floatOperation,
218
    Operation op, bool rhs_is_smi) {
219 220
  Label do_float_operation(this), end(this), call_stub(this),
      check_rhsisoddball(this, Label::kDeferred), call_with_any_feedback(this),
221 222
      if_lhsisnotnumber(this, Label::kDeferred),
      if_bigint(this, Label::kDeferred);
223 224
  VARIABLE(var_float_lhs, MachineRepresentation::kFloat64);
  VARIABLE(var_float_rhs, MachineRepresentation::kFloat64);
225 226
  VARIABLE(var_type_feedback, MachineRepresentation::kTaggedSigned);
  VARIABLE(var_result, MachineRepresentation::kTagged);
227

228 229 230 231 232 233 234 235
  Label if_lhsissmi(this);
  // If rhs is known to be an Smi (in the SubSmi, MulSmi, DivSmi, ModSmi
  // bytecode handlers) we want to fast path Smi operation. For the normal
  // operation, we want to fast path both Smi and Number operations, so this
  // path should not be marked as Deferred.
  Label if_lhsisnotsmi(this,
                       rhs_is_smi ? Label::kDeferred : Label::kNonDeferred);
  Branch(TaggedIsNotSmi(lhs), &if_lhsisnotsmi, &if_lhsissmi);
236

237
  // Check if the {lhs} is a Smi or a HeapObject.
238
  BIND(&if_lhsissmi);
239
  {
240 241 242 243 244 245
    Comment("lhs is Smi");
    if (!rhs_is_smi) {
      // Check if the {rhs} is also a Smi.
      Label if_rhsissmi(this), if_rhsisnotsmi(this);
      Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
      BIND(&if_rhsisnotsmi);
246
      {
247 248 249 250 251 252 253
        // Check if {rhs} is a HeapNumber.
        GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);

        // Perform a floating point operation.
        var_float_lhs.Bind(SmiToFloat64(lhs));
        var_float_rhs.Bind(LoadHeapNumberValue(rhs));
        Goto(&do_float_operation);
254 255
      }

256
      BIND(&if_rhsissmi);
257 258 259
    }

    {
260 261 262
      Comment("perform smi operation");
      var_result.Bind(smiOperation(lhs, rhs, &var_type_feedback));
      Goto(&end);
263 264 265
    }
  }

266
  BIND(&if_lhsisnotsmi);
267
  {
268
    Comment("lhs is not Smi");
269
    // Check if the {lhs} is a HeapNumber.
270
    GotoIfNot(IsHeapNumber(lhs), &if_lhsisnotnumber);
271

272 273 274 275
    if (!rhs_is_smi) {
      // Check if the {rhs} is a Smi.
      Label if_rhsissmi(this), if_rhsisnotsmi(this);
      Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
276

277 278 279 280 281 282 283 284 285 286 287 288
      BIND(&if_rhsisnotsmi);
      {
        // Check if the {rhs} is a HeapNumber.
        GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);

        // Perform a floating point operation.
        var_float_lhs.Bind(LoadHeapNumberValue(lhs));
        var_float_rhs.Bind(LoadHeapNumberValue(rhs));
        Goto(&do_float_operation);
      }

      BIND(&if_rhsissmi);
289 290 291
    }

    {
292
      // Perform floating point operation.
293 294 295
      var_float_lhs.Bind(LoadHeapNumberValue(lhs));
      var_float_rhs.Bind(SmiToFloat64(rhs));
      Goto(&do_float_operation);
296 297 298
    }
  }

299
  BIND(&do_float_operation);
300
  {
301
    var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNumber));
302 303 304
    Node* lhs_value = var_float_lhs.value();
    Node* rhs_value = var_float_rhs.value();
    Node* value = floatOperation(lhs_value, rhs_value);
305 306 307 308
    var_result.Bind(AllocateHeapNumberWithValue(value));
    Goto(&end);
  }

309
  BIND(&if_lhsisnotnumber);
310 311
  {
    // No checks on rhs are done yet. We just know lhs is not a number or Smi.
312
    Label if_left_bigint(this), if_left_oddball(this);
313
    Node* lhs_instance_type = LoadInstanceType(lhs);
314
    GotoIf(IsBigIntInstanceType(lhs_instance_type), &if_left_bigint);
315
    Node* lhs_is_oddball = InstanceTypeEqual(lhs_instance_type, ODDBALL_TYPE);
316
    Branch(lhs_is_oddball, &if_left_oddball, &call_with_any_feedback);
317

318
    BIND(&if_left_oddball);
319
    {
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
      Label if_rhsissmi(this), if_rhsisnotsmi(this);
      Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);

      BIND(&if_rhsissmi);
      {
        var_type_feedback.Bind(
            SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
        Goto(&call_stub);
      }

      BIND(&if_rhsisnotsmi);
      {
        // Check if {rhs} is a HeapNumber.
        GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);

        var_type_feedback.Bind(
            SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
        Goto(&call_stub);
      }
339 340
    }

341
    BIND(&if_left_bigint);
342
    {
343 344
      GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);
      Branch(IsBigInt(rhs), &if_bigint, &call_with_any_feedback);
345 346 347
    }
  }

348
  BIND(&check_rhsisoddball);
349 350 351 352
  {
    // Check if rhs is an oddball. At this point we know lhs is either a
    // Smi or number or oddball and rhs is not a number or Smi.
    Node* rhs_instance_type = LoadInstanceType(rhs);
353
    GotoIf(IsBigIntInstanceType(rhs_instance_type), &if_bigint);
354
    Node* rhs_is_oddball = InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE);
355 356 357 358
    GotoIfNot(rhs_is_oddball, &call_with_any_feedback);

    var_type_feedback.Bind(
        SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
359
    Goto(&call_stub);
360 361
  }

362 363 364 365 366
  // This handles the case where at least one input is a BigInt.
  BIND(&if_bigint);
  {
    var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kBigInt));
    var_result.Bind(CallRuntime(Runtime::kBigIntBinaryOp, context, lhs, rhs,
367
                                SmiConstant(op)));
368 369 370
    Goto(&end);
  }

371
  BIND(&call_with_any_feedback);
372 373
  {
    var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny));
374 375 376 377 378 379
    Goto(&call_stub);
  }

  BIND(&call_stub);
  {
    Node* result;
380 381
    switch (op) {
      case Operation::kSubtract:
382 383
        result = CallBuiltin(Builtins::kSubtract, context, lhs, rhs);
        break;
384
      case Operation::kMultiply:
385 386
        result = CallBuiltin(Builtins::kMultiply, context, lhs, rhs);
        break;
387
      case Operation::kDivide:
388 389
        result = CallBuiltin(Builtins::kDivide, context, lhs, rhs);
        break;
390
      case Operation::kModulus:
391 392 393 394 395 396
        result = CallBuiltin(Builtins::kModulus, context, lhs, rhs);
        break;
      default:
        UNREACHABLE();
    }
    var_result.Bind(result);
397 398 399
    Goto(&end);
  }

400
  BIND(&end);
401
  UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_id);
402 403 404
  return var_result.value();
}

405
Node* BinaryOpAssembler::Generate_SubtractWithFeedback(Node* context, Node* lhs,
406
                                                       Node* rhs, Node* slot_id,
407
                                                       Node* feedback_vector,
408 409
                                                       bool rhs_is_smi) {
  auto smiFunction = [=](Node* lhs, Node* rhs, Variable* var_type_feedback) {
410 411
    Label end(this);
    TVARIABLE(Number, var_result);
412 413 414 415 416
    // If rhs is known to be an Smi (for SubSmi) we want to fast path Smi
    // operation. For the normal Sub operation, we want to fast path both
    // Smi and Number operations, so this path should not be marked as Deferred.
    Label if_overflow(this,
                      rhs_is_smi ? Label::kDeferred : Label::kNonDeferred);
417 418 419
    var_result = TrySmiSub(CAST(lhs), CAST(rhs), &if_overflow);
    var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kSignedSmall));
    Goto(&end);
420

421
    BIND(&if_overflow);
422
    {
423
      var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kNumber));
424
      Node* value = Float64Sub(SmiToFloat64(lhs), SmiToFloat64(rhs));
425
      var_result = AllocateHeapNumberWithValue(value);
426
      Goto(&end);
427 428
    }

429 430 431 432 433 434 435
    BIND(&end);
    return var_result.value();
  };
  auto floatFunction = [=](Node* lhs, Node* rhs) {
    return Float64Sub(lhs, rhs);
  };
  return Generate_BinaryOperationWithFeedback(
436
      context, lhs, rhs, slot_id, feedback_vector, smiFunction, floatFunction,
437
      Operation::kSubtract, rhs_is_smi);
438
}
439

440 441 442 443 444
Node* BinaryOpAssembler::Generate_MultiplyWithFeedback(Node* context, Node* lhs,
                                                       Node* rhs, Node* slot_id,
                                                       Node* feedback_vector,
                                                       bool rhs_is_smi) {
  auto smiFunction = [=](Node* lhs, Node* rhs, Variable* var_type_feedback) {
445
    TNode<Number> result = SmiMul(CAST(lhs), CAST(rhs));
446 447
    var_type_feedback->Bind(SelectSmiConstant(
        TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall,
448
        BinaryOperationFeedback::kNumber));
449 450 451 452 453 454
    return result;
  };
  auto floatFunction = [=](Node* lhs, Node* rhs) {
    return Float64Mul(lhs, rhs);
  };
  return Generate_BinaryOperationWithFeedback(
455
      context, lhs, rhs, slot_id, feedback_vector, smiFunction, floatFunction,
456
      Operation::kMultiply, rhs_is_smi);
457 458
}

459 460
Node* BinaryOpAssembler::Generate_DivideWithFeedback(
    Node* context, Node* dividend, Node* divisor, Node* slot_id,
461
    Node* feedback_vector, bool rhs_is_smi) {
462 463 464 465 466 467 468
  auto smiFunction = [=](Node* lhs, Node* rhs, Variable* var_type_feedback) {
    VARIABLE(var_result, MachineRepresentation::kTagged);
    // If rhs is known to be an Smi (for DivSmi) we want to fast path Smi
    // operation. For the normal Div operation, we want to fast path both
    // Smi and Number operations, so this path should not be marked as Deferred.
    Label bailout(this, rhs_is_smi ? Label::kDeferred : Label::kNonDeferred),
        end(this);
469
    var_result.Bind(TrySmiDiv(CAST(lhs), CAST(rhs), &bailout));
470 471
    var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kSignedSmall));
    Goto(&end);
472

473
    BIND(&bailout);
474
    {
475 476
      var_type_feedback->Bind(
          SmiConstant(BinaryOperationFeedback::kSignedSmallInputs));
477 478
      Node* value = Float64Div(SmiToFloat64(lhs), SmiToFloat64(rhs));
      var_result.Bind(AllocateHeapNumberWithValue(value));
479 480 481
      Goto(&end);
    }

482 483 484 485 486 487 488
    BIND(&end);
    return var_result.value();
  };
  auto floatFunction = [=](Node* lhs, Node* rhs) {
    return Float64Div(lhs, rhs);
  };
  return Generate_BinaryOperationWithFeedback(
489
      context, dividend, divisor, slot_id, feedback_vector, smiFunction,
490
      floatFunction, Operation::kDivide, rhs_is_smi);
491 492
}

493 494
Node* BinaryOpAssembler::Generate_ModulusWithFeedback(
    Node* context, Node* dividend, Node* divisor, Node* slot_id,
495
    Node* feedback_vector, bool rhs_is_smi) {
496
  auto smiFunction = [=](Node* lhs, Node* rhs, Variable* var_type_feedback) {
497
    TNode<Number> result = SmiMod(CAST(lhs), CAST(rhs));
498 499
    var_type_feedback->Bind(SelectSmiConstant(
        TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall,
500
        BinaryOperationFeedback::kNumber));
501 502 503 504 505 506
    return result;
  };
  auto floatFunction = [=](Node* lhs, Node* rhs) {
    return Float64Mod(lhs, rhs);
  };
  return Generate_BinaryOperationWithFeedback(
507
      context, dividend, divisor, slot_id, feedback_vector, smiFunction,
508
      floatFunction, Operation::kModulus, rhs_is_smi);
509 510
}

511 512 513 514 515 516 517 518 519
Node* BinaryOpAssembler::Generate_ExponentiateWithFeedback(
    Node* context, Node* base, Node* exponent, Node* slot_id,
    Node* feedback_vector, bool rhs_is_smi) {
  // We currently don't optimize exponentiation based on feedback.
  Node* dummy_feedback = SmiConstant(BinaryOperationFeedback::kAny);
  UpdateFeedback(dummy_feedback, feedback_vector, slot_id);
  return CallBuiltin(Builtins::kExponentiate, context, base, exponent);
}

520 521
}  // namespace internal
}  // namespace v8