test-js-typed-lowering.cc 33.7 KB
Newer Older
1 2 3 4
// Copyright 2014 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/codegen/tick-counter.h"
6
#include "src/compiler/js-graph.h"
7
#include "src/compiler/js-heap-broker.h"
8
#include "src/compiler/js-typed-lowering.h"
9
#include "src/compiler/machine-operator.h"
10
#include "src/compiler/node-properties.h"
11
#include "src/compiler/opcodes.h"
12
#include "src/compiler/operator-properties.h"
13
#include "src/compiler/simplified-operator.h"
14
#include "src/compiler/typer.h"
15
#include "src/execution/isolate.h"
16
#include "src/heap/factory-inl.h"
17
#include "src/objects/objects.h"
18
#include "test/cctest/cctest.h"
19

20 21 22
namespace v8 {
namespace internal {
namespace compiler {
23 24 25

class JSTypedLoweringTester : public HandleAndZoneScope {
 public:
26
  explicit JSTypedLoweringTester(int num_parameters = 0)
27
      : isolate(main_isolate()),
28
        canonical(isolate),
29
        js_heap_broker(isolate, main_zone(), FLAG_trace_heap_broker),
30 31
        binop(nullptr),
        unop(nullptr),
32
        javascript(main_zone()),
33
        machine(main_zone()),
34 35 36
        simplified(main_zone()),
        common(main_zone()),
        graph(main_zone()),
37
        typer(&js_heap_broker, Typer::kNoFlags, &graph, &tick_counter),
38
        context_node(nullptr) {
39
    graph.SetStart(graph.NewNode(common.Start(num_parameters)));
40
    graph.SetEnd(graph.NewNode(common.End(1), graph.start()));
41
    typer.Run();
42 43 44
  }

  Isolate* isolate;
45
  TickCounter tick_counter;
46
  CanonicalHandleScope canonical;
47
  JSHeapBroker js_heap_broker;
48 49
  const Operator* binop;
  const Operator* unop;
50 51 52 53 54 55 56
  JSOperatorBuilder javascript;
  MachineOperatorBuilder machine;
  SimplifiedOperatorBuilder simplified;
  CommonOperatorBuilder common;
  Graph graph;
  Typer typer;
  Node* context_node;
57 58
  BinaryOperationHint const binop_hints = BinaryOperationHint::kAny;
  CompareOperationHint const compare_hints = CompareOperationHint::kAny;
59

60
  Node* Parameter(Type t, int32_t index = 0) {
61
    Node* n = graph.NewNode(common.Parameter(index), graph.start());
62
    NodeProperties::SetType(n, t);
63 64 65
    return n;
  }

66
  Node* UndefinedConstant() {
67 68
    Handle<HeapObject> value = isolate->factory()->undefined_value();
    return graph.NewNode(common.HeapConstant(value));
69 70
  }

71
  Node* HeapConstant(Handle<HeapObject> constant) {
72
    return graph.NewNode(common.HeapConstant(constant));
73 74
  }

75
  Node* EmptyFrameState(Node* context) {
76 77 78 79 80 81
    Node* parameters =
        graph.NewNode(common.StateValues(0, SparseInputMask::Dense()));
    Node* locals =
        graph.NewNode(common.StateValues(0, SparseInputMask::Dense()));
    Node* stack =
        graph.NewNode(common.StateValues(0, SparseInputMask::Dense()));
82

83 84 85
    Node* state_node = graph.NewNode(
        common.FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
                          nullptr),
86
        parameters, locals, stack, context, UndefinedConstant(), graph.start());
87 88 89 90

    return state_node;
  }

91
  Node* reduce(Node* node) {
92 93
    JSGraph jsgraph(main_isolate(), &graph, &common, &javascript, &simplified,
                    &machine);
94
    // TODO(titzer): mock the GraphReducer here for better unit testing.
95
    GraphReducer graph_reducer(main_zone(), &graph, &tick_counter);
96 97
    JSTypedLowering reducer(&graph_reducer, &jsgraph, &js_heap_broker,
                            main_zone());
98 99 100 101 102
    Reduction reduction = reducer.Reduce(node);
    if (reduction.Changed()) return reduction.replacement();
    return node;
  }

103
  Node* start() { return graph.start(); }
104 105

  Node* context() {
106
    if (context_node == nullptr) {
107
      context_node = graph.NewNode(common.Parameter(-1), graph.start());
108 109 110 111 112 113
    }
    return context_node;
  }

  Node* control() { return start(); }

114
  void CheckBinop(IrOpcode::Value expected, Node* node) {
115 116 117
    CHECK_EQ(expected, node->opcode());
  }

118
  void CheckBinop(const Operator* expected, Node* node) {
119 120 121
    CHECK_EQ(expected->opcode(), node->op()->opcode());
  }

122
  Node* ReduceUnop(const Operator* op, Type input_type) {
123 124 125
    return reduce(Unop(op, Parameter(input_type)));
  }

126
  Node* ReduceBinop(const Operator* op, Type left_type, Type right_type) {
127 128 129
    return reduce(Binop(op, Parameter(left_type, 0), Parameter(right_type, 1)));
  }

130
  Node* Binop(const Operator* op, Node* left, Node* right) {
131
    // JS binops also require context, effect, and control
132 133 134 135 136 137 138 139 140 141 142 143 144 145
    std::vector<Node*> inputs;
    inputs.push_back(left);
    inputs.push_back(right);
    if (OperatorProperties::HasContextInput(op)) {
      inputs.push_back(context());
    }
    for (int i = 0; i < OperatorProperties::GetFrameStateInputCount(op); i++) {
      inputs.push_back(EmptyFrameState(context()));
    }
    if (op->EffectInputCount() > 0) {
      inputs.push_back(start());
    }
    if (op->ControlInputCount() > 0) {
      inputs.push_back(control());
146
    }
147 148
    return graph.NewNode(op, static_cast<int>(inputs.size()),
                         &(inputs.front()));
149 150
  }

151
  Node* Unop(const Operator* op, Node* input) {
152
    // JS unops also require context, effect, and control
153
    if (OperatorProperties::GetFrameStateInputCount(op) > 0) {
154
      CHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(op));
155 156 157 158 159
      return graph.NewNode(op, input, context(), EmptyFrameState(context()),
                           start(), control());
    } else {
      return graph.NewNode(op, input, context(), start(), control());
    }
160 161 162
  }

  Node* UseForEffect(Node* node) {
163 164
    Node* merge = graph.NewNode(common.Merge(1), start());
    return graph.NewNode(common.EffectPhi(1), node, merge);
165 166 167 168 169 170 171 172
  }

  void CheckEffectInput(Node* effect, Node* use) {
    CHECK_EQ(effect, NodeProperties::GetEffectInput(use));
  }

  void CheckNumberConstant(double expected, Node* result) {
    CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
173
    CHECK_EQ(expected, OpParameter<double>(result->op()));
174 175 176 177
  }

  void CheckNaN(Node* result) {
    CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
178
    double value = OpParameter<double>(result->op());
179 180 181 182 183 184 185 186 187 188 189
    CHECK(std::isnan(value));
  }

  void CheckTrue(Node* result) {
    CheckHandle(isolate->factory()->true_value(), result);
  }

  void CheckFalse(Node* result) {
    CheckHandle(isolate->factory()->false_value(), result);
  }

190
  void CheckHandle(Handle<HeapObject> expected, Node* result) {
191
    CHECK_EQ(IrOpcode::kHeapConstant, result->opcode());
192
    Handle<HeapObject> value = HeapConstantOf(result->op());
193 194 195 196
    CHECK_EQ(*expected, *value);
  }
};

197
static Type kStringTypes[] = {Type::InternalizedString(), Type::String()};
198

199 200 201 202
static Type kInt32Types[] = {Type::UnsignedSmall(), Type::Negative32(),
                             Type::Unsigned31(),    Type::SignedSmall(),
                             Type::Signed32(),      Type::Unsigned32(),
                             Type::Integral32()};
203

204
static Type kNumberTypes[] = {
205 206 207 208
    Type::UnsignedSmall(), Type::Negative32(),  Type::Unsigned31(),
    Type::SignedSmall(),   Type::Signed32(),    Type::Unsigned32(),
    Type::Integral32(),    Type::MinusZero(),   Type::NaN(),
    Type::OrderedNumber(), Type::PlainNumber(), Type::Number()};
209

210
static Type I32Type(bool is_signed) {
211 212 213 214 215 216 217 218 219
  return is_signed ? Type::Signed32() : Type::Unsigned32();
}


static IrOpcode::Value NumberToI32(bool is_signed) {
  return is_signed ? IrOpcode::kNumberToInt32 : IrOpcode::kNumberToUint32;
}


220 221
// TODO(turbofan): Lowering of StringAdd is disabled for now.
#if 0
222
TEST(StringBinops) {
223 224
  JSTypedLoweringTester R;

225
  for (size_t i = 0; i < arraysize(kStringTypes); ++i) {
226 227
    Node* p0 = R.Parameter(kStringTypes[i], 0);

228
    for (size_t j = 0; j < arraysize(kStringTypes); ++j) {
229 230
      Node* p1 = R.Parameter(kStringTypes[j], 1);

231
      Node* add = R.Binop(R.javascript.Add(), p0, p1);
232 233
      Node* r = R.reduce(add);

234
      R.CheckBinop(IrOpcode::kStringAdd, r);
235 236 237 238 239
      CHECK_EQ(p0, r->InputAt(0));
      CHECK_EQ(p1, r->InputAt(1));
    }
  }
}
240
#endif
241

242
TEST(AddNumber1) {
243
  JSTypedLoweringTester R;
244
  for (size_t i = 0; i < arraysize(kNumberTypes); ++i) {
245 246
    Node* p0 = R.Parameter(kNumberTypes[i], 0);
    Node* p1 = R.Parameter(kNumberTypes[i], 1);
247
    Node* add = R.Binop(R.javascript.Add(BinaryOperationHint::kAny), p0, p1);
248 249
    Node* r = R.reduce(add);

250
    R.CheckBinop(IrOpcode::kNumberAdd, r);
251 252 253 254 255
    CHECK_EQ(p0, r->InputAt(0));
    CHECK_EQ(p1, r->InputAt(1));
  }
}

256
TEST(NumberBinops) {
257
  JSTypedLoweringTester R;
258
  const Operator* ops[] = {
259 260 261 262 263
      R.javascript.Add(R.binop_hints), R.simplified.NumberAdd(),
      R.javascript.Subtract(),         R.simplified.NumberSubtract(),
      R.javascript.Multiply(),         R.simplified.NumberMultiply(),
      R.javascript.Divide(),           R.simplified.NumberDivide(),
      R.javascript.Modulus(),          R.simplified.NumberModulus(),
264 265
  };

266
  for (size_t i = 0; i < arraysize(kNumberTypes); ++i) {
267 268
    Node* p0 = R.Parameter(kNumberTypes[i], 0);

269
    for (size_t j = 0; j < arraysize(kNumberTypes); ++j) {
270 271
      Node* p1 = R.Parameter(kNumberTypes[j], 1);

272
      for (size_t k = 0; k < arraysize(ops); k += 2) {
273 274 275
        Node* add = R.Binop(ops[k], p0, p1);
        Node* r = R.reduce(add);

276
        R.CheckBinop(ops[k + 1], r);
277 278 279 280 281 282 283 284 285
        CHECK_EQ(p0, r->InputAt(0));
        CHECK_EQ(p1, r->InputAt(1));
      }
    }
  }
}


static void CheckToI32(Node* old_input, Node* new_input, bool is_signed) {
286 287 288
  Type old_type = NodeProperties::GetType(old_input);
  Type new_type = NodeProperties::GetType(new_input);
  Type expected_type = I32Type(is_signed);
289 290
  CHECK(new_type.Is(expected_type));
  if (old_type.Is(expected_type)) {
291 292
    CHECK_EQ(old_input, new_input);
  } else if (new_input->opcode() == IrOpcode::kNumberConstant) {
293
    double v = OpParameter<double>(new_input->op());
294 295 296 297 298 299 300 301 302
    double e = static_cast<double>(is_signed ? FastD2I(v) : FastD2UI(v));
    CHECK_EQ(e, v);
  }
}


// A helper class for testing lowering of bitwise shift operators.
class JSBitwiseShiftTypedLoweringTester : public JSTypedLoweringTester {
 public:
303
  JSBitwiseShiftTypedLoweringTester() : JSTypedLoweringTester() {
304
    int i = 0;
305
    set(i++, javascript.ShiftLeft(), true);
306
    set(i++, simplified.NumberShiftLeft(), false);
307
    set(i++, javascript.ShiftRight(), true);
308
    set(i++, simplified.NumberShiftRight(), false);
309
    set(i++, javascript.ShiftRightLogical(), false);
310
    set(i++, simplified.NumberShiftRightLogical(), false);
311
  }
312 313 314
  static const int kNumberOps = 6;
  const Operator* ops[kNumberOps];
  bool signedness[kNumberOps];
315

316
 private:
317
  void set(int idx, const Operator* op, bool s) {
318 319
    ops[idx] = op;
    signedness[idx] = s;
320 321 322 323 324
  }
};


TEST(Int32BitwiseShifts) {
325
  JSBitwiseShiftTypedLoweringTester R;
326

327
  Type types[] = {
328 329 330 331 332
      Type::SignedSmall(), Type::UnsignedSmall(), Type::Negative32(),
      Type::Unsigned31(),  Type::Unsigned32(),    Type::Signed32(),
      Type::MinusZero(),   Type::NaN(),           Type::Undefined(),
      Type::Null(),        Type::Boolean(),       Type::Number(),
      Type::PlainNumber(), Type::String()};
333

334
  for (size_t i = 0; i < arraysize(types); ++i) {
335 336
    Node* p0 = R.Parameter(types[i], 0);

337
    for (size_t j = 0; j < arraysize(types); ++j) {
338 339 340 341 342 343
      Node* p1 = R.Parameter(types[j], 1);

      for (int k = 0; k < R.kNumberOps; k += 2) {
        Node* add = R.Binop(R.ops[k], p0, p1);
        Node* r = R.reduce(add);

344
        R.CheckBinop(R.ops[k + 1], r);
345 346 347 348
        Node* r0 = r->InputAt(0);
        Node* r1 = r->InputAt(1);

        CheckToI32(p0, r0, R.signedness[k]);
349
        CheckToI32(p1, r1, false);
350 351 352 353 354 355 356 357 358
      }
    }
  }
}


// A helper class for testing lowering of bitwise operators.
class JSBitwiseTypedLoweringTester : public JSTypedLoweringTester {
 public:
359
  JSBitwiseTypedLoweringTester() : JSTypedLoweringTester() {
360
    int i = 0;
361
    set(i++, javascript.BitwiseOr(), true);
362
    set(i++, simplified.NumberBitwiseOr(), true);
363
    set(i++, javascript.BitwiseXor(), true);
364
    set(i++, simplified.NumberBitwiseXor(), true);
365
    set(i++, javascript.BitwiseAnd(), true);
366
    set(i++, simplified.NumberBitwiseAnd(), true);
367
  }
368 369 370
  static const int kNumberOps = 6;
  const Operator* ops[kNumberOps];
  bool signedness[kNumberOps];
371

372
 private:
373
  void set(int idx, const Operator* op, bool s) {
374 375
    ops[idx] = op;
    signedness[idx] = s;
376 377 378 379 380
  }
};


TEST(Int32BitwiseBinops) {
381
  JSBitwiseTypedLoweringTester R;
382

383
  Type types[] = {
384 385 386 387 388
      Type::SignedSmall(),   Type::UnsignedSmall(), Type::Unsigned32(),
      Type::Signed32(),      Type::MinusZero(),     Type::NaN(),
      Type::OrderedNumber(), Type::PlainNumber(),   Type::Undefined(),
      Type::Null(),          Type::Boolean(),       Type::Number(),
      Type::String()};
389

390
  for (size_t i = 0; i < arraysize(types); ++i) {
391 392
    Node* p0 = R.Parameter(types[i], 0);

393
    for (size_t j = 0; j < arraysize(types); ++j) {
394 395 396 397 398 399
      Node* p1 = R.Parameter(types[j], 1);

      for (int k = 0; k < R.kNumberOps; k += 2) {
        Node* add = R.Binop(R.ops[k], p0, p1);
        Node* r = R.reduce(add);

400
        R.CheckBinop(R.ops[k + 1], r);
401 402 403 404 405 406 407 408 409 410 411

        CheckToI32(p0, r->InputAt(0), R.signedness[k]);
        CheckToI32(p1, r->InputAt(1), R.signedness[k + 1]);
      }
    }
  }
}


TEST(JSToNumber1) {
  JSTypedLoweringTester R;
412
  const Operator* ton = R.javascript.ToNumber();
413

414
  for (size_t i = 0; i < arraysize(kNumberTypes); i++) {  // ToNumber(number)
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
    Node* r = R.ReduceUnop(ton, kNumberTypes[i]);
    CHECK_EQ(IrOpcode::kParameter, r->opcode());
  }

  {  // ToNumber(undefined)
    Node* r = R.ReduceUnop(ton, Type::Undefined());
    R.CheckNaN(r);
  }

  {  // ToNumber(null)
    Node* r = R.ReduceUnop(ton, Type::Null());
    R.CheckNumberConstant(0.0, r);
  }
}


TEST(JSToNumber_replacement) {
  JSTypedLoweringTester R;

434
  Type types[] = {Type::Null(), Type::Undefined(), Type::Number()};
435

436
  for (size_t i = 0; i < arraysize(types); i++) {
437
    Node* n = R.Parameter(types[i]);
438 439 440
    Node* c =
        R.graph.NewNode(R.javascript.ToNumber(), n, R.context(),
                        R.EmptyFrameState(R.context()), R.start(), R.start());
441
    Node* effect_use = R.UseForEffect(c);
442
    Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(), n, c);
443 444 445 446

    R.CheckEffectInput(c, effect_use);
    Node* r = R.reduce(c);

447
    if (types[i].Is(Type::Number())) {
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
      CHECK_EQ(n, r);
    } else {
      CHECK_EQ(IrOpcode::kNumberConstant, r->opcode());
    }

    CHECK_EQ(n, add->InputAt(0));
    CHECK_EQ(r, add->InputAt(1));
    R.CheckEffectInput(R.start(), effect_use);
  }
}


TEST(JSToNumberOfConstant) {
  JSTypedLoweringTester R;

463 464 465
  const Operator* ops[] = {R.common.NumberConstant(0),
                           R.common.NumberConstant(-1),
                           R.common.NumberConstant(0.1)};
466

467
  for (size_t i = 0; i < arraysize(ops); i++) {
468 469 470 471 472 473
    Node* n = R.graph.NewNode(ops[i]);
    Node* convert = R.Unop(R.javascript.ToNumber(), n);
    Node* r = R.reduce(convert);
    // Note that either outcome below is correct. It only depends on whether
    // the types of constants are eagerly computed or only computed by the
    // typing pass.
474
    if (NodeProperties::GetType(n).Is(Type::Number())) {
475 476 477 478 479 480 481 482 483 484 485 486 487 488
      // If number constants are eagerly typed, then reduction should
      // remove the ToNumber.
      CHECK_EQ(n, r);
    } else {
      // Otherwise, type-based lowering should only look at the type, and
      // *not* try to constant fold.
      CHECK_EQ(convert, r);
    }
  }
}


TEST(JSToNumberOfNumberOrOtherPrimitive) {
  JSTypedLoweringTester R;
489 490
  Type others[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
                   Type::String()};
491

492
  for (size_t i = 0; i < arraysize(others); i++) {
493
    Type t = Type::Union(Type::Number(), others[i], R.main_zone());
494
    Node* r = R.ReduceUnop(R.javascript.ToNumber(), t);
495
    CHECK_EQ(IrOpcode::kPlainPrimitiveToNumber, r->opcode());
496 497 498 499 500 501 502
  }
}


TEST(JSToString1) {
  JSTypedLoweringTester R;

503
  for (size_t i = 0; i < arraysize(kStringTypes); i++) {
504 505 506 507
    Node* r = R.ReduceUnop(R.javascript.ToString(), kStringTypes[i]);
    CHECK_EQ(IrOpcode::kParameter, r->opcode());
  }

508
  const Operator* op = R.javascript.ToString();
509 510 511 512 513 514 515 516 517 518 519 520 521

  {  // ToString(undefined) => "undefined"
    Node* r = R.ReduceUnop(op, Type::Undefined());
    R.CheckHandle(R.isolate->factory()->undefined_string(), r);
  }

  {  // ToString(null) => "null"
    Node* r = R.ReduceUnop(op, Type::Null());
    R.CheckHandle(R.isolate->factory()->null_string(), r);
  }

  {  // ToString(boolean)
    Node* r = R.ReduceUnop(op, Type::Boolean());
522
    CHECK_EQ(IrOpcode::kSelect, r->opcode());
523 524 525 526
  }

  {  // ToString(number)
    Node* r = R.ReduceUnop(op, Type::Number());
527
    CHECK_EQ(IrOpcode::kNumberToString, r->opcode());
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
  }

  {  // ToString(string)
    Node* r = R.ReduceUnop(op, Type::String());
    CHECK_EQ(IrOpcode::kParameter, r->opcode());  // No-op
  }

  {  // ToString(object)
    Node* r = R.ReduceUnop(op, Type::Object());
    CHECK_EQ(IrOpcode::kJSToString, r->opcode());  // No reduction.
  }
}


TEST(JSToString_replacement) {
  JSTypedLoweringTester R;

545
  Type types[] = {Type::Null(), Type::Undefined(), Type::String()};
546

547
  for (size_t i = 0; i < arraysize(types); i++) {
548
    Node* n = R.Parameter(types[i]);
549 550 551
    Node* c =
        R.graph.NewNode(R.javascript.ToString(), n, R.context(),
                        R.EmptyFrameState(R.context()), R.start(), R.start());
552
    Node* effect_use = R.UseForEffect(c);
553
    Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(), n, c);
554 555 556 557

    R.CheckEffectInput(c, effect_use);
    Node* r = R.reduce(c);

558
    if (types[i].Is(Type::String())) {
559 560 561 562 563 564 565 566 567 568 569
      CHECK_EQ(n, r);
    } else {
      CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
    }

    CHECK_EQ(n, add->InputAt(0));
    CHECK_EQ(r, add->InputAt(1));
    R.CheckEffectInput(R.start(), effect_use);
  }
}

570
TEST(StringComparison) {
571 572
  JSTypedLoweringTester R;

573
  const Operator* ops[] = {
574
      R.javascript.LessThan(CompareOperationHint::kAny),
575
      R.simplified.StringLessThan(),
576
      R.javascript.LessThanOrEqual(CompareOperationHint::kAny),
577
      R.simplified.StringLessThanOrEqual(),
578
      R.javascript.GreaterThan(CompareOperationHint::kAny),
579
      R.simplified.StringLessThan(),
580
      R.javascript.GreaterThanOrEqual(CompareOperationHint::kAny),
581
      R.simplified.StringLessThanOrEqual()};
582

583
  for (size_t i = 0; i < arraysize(kStringTypes); i++) {
584
    Node* p0 = R.Parameter(kStringTypes[i], 0);
585
    for (size_t j = 0; j < arraysize(kStringTypes); j++) {
586 587
      Node* p1 = R.Parameter(kStringTypes[j], 1);

588
      for (size_t k = 0; k < arraysize(ops); k += 2) {
589 590 591
        Node* cmp = R.Binop(ops[k], p0, p1);
        Node* r = R.reduce(cmp);

592
        R.CheckBinop(ops[k + 1], r);
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
        if (k >= 4) {
          // GreaterThan and GreaterThanOrEqual commute the inputs
          // and use the LessThan and LessThanOrEqual operators.
          CHECK_EQ(p1, r->InputAt(0));
          CHECK_EQ(p0, r->InputAt(1));
        } else {
          CHECK_EQ(p0, r->InputAt(0));
          CHECK_EQ(p1, r->InputAt(1));
        }
      }
    }
  }
}


static void CheckIsConvertedToNumber(Node* val, Node* converted) {
609
  if (NodeProperties::GetType(val).Is(Type::Number())) {
610 611 612
    CHECK_EQ(val, converted);
  } else {
    if (converted->opcode() == IrOpcode::kNumberConstant) return;
613 614
    CHECK(IrOpcode::kJSToNumber == converted->opcode() ||
          IrOpcode::kJSToNumberConvertBigInt == converted->opcode());
615 616 617 618
    CHECK_EQ(val, converted->InputAt(0));
  }
}

619
TEST(NumberComparison) {
620 621
  JSTypedLoweringTester R;

622
  const Operator* ops[] = {
623
      R.javascript.LessThan(CompareOperationHint::kAny),
624
      R.simplified.NumberLessThan(),
625
      R.javascript.LessThanOrEqual(CompareOperationHint::kAny),
626
      R.simplified.NumberLessThanOrEqual(),
627
      R.javascript.GreaterThan(CompareOperationHint::kAny),
628
      R.simplified.NumberLessThan(),
629
      R.javascript.GreaterThanOrEqual(CompareOperationHint::kAny),
630
      R.simplified.NumberLessThanOrEqual()};
631

632 633
  Node* const p0 = R.Parameter(Type::Number(), 0);
  Node* const p1 = R.Parameter(Type::Number(), 1);
634

635 636 637
  for (size_t k = 0; k < arraysize(ops); k += 2) {
    Node* cmp = R.Binop(ops[k], p0, p1);
    Node* r = R.reduce(cmp);
638

639
    R.CheckBinop(ops[k + 1], r);
640 641 642 643 644 645 646 647
    if (k >= 4) {
      // GreaterThan and GreaterThanOrEqual commute the inputs
      // and use the LessThan and LessThanOrEqual operators.
      CheckIsConvertedToNumber(p1, r->InputAt(0));
      CheckIsConvertedToNumber(p0, r->InputAt(1));
    } else {
      CheckIsConvertedToNumber(p0, r->InputAt(0));
      CheckIsConvertedToNumber(p1, r->InputAt(1));
648 649 650 651
    }
  }
}

652
TEST(MixedComparison1) {
653 654
  JSTypedLoweringTester R;

655 656
  Type types[] = {Type::Number(), Type::String(),
                  Type::Union(Type::Number(), Type::String(), R.main_zone())};
657

658
  for (size_t i = 0; i < arraysize(types); i++) {
659 660
    Node* p0 = R.Parameter(types[i], 0);

661
    for (size_t j = 0; j < arraysize(types); j++) {
662 663
      Node* p1 = R.Parameter(types[j], 1);
      {
664
        const Operator* less_than =
665
            R.javascript.LessThan(CompareOperationHint::kAny);
666
        Node* cmp = R.Binop(less_than, p0, p1);
667
        Node* r = R.reduce(cmp);
668
        if (types[i].Is(Type::String()) && types[j].Is(Type::String())) {
669
          R.CheckBinop(R.simplified.StringLessThan(), r);
670 671 672 673
        } else if ((types[i].Is(Type::Number()) &&
                    types[j].Is(Type::Number())) ||
                   (!types[i].Maybe(Type::String()) ||
                    !types[j].Maybe(Type::String()))) {
674
          R.CheckBinop(R.simplified.NumberLessThan(), r);
675
        } else {
676 677
          // No reduction of mixed types.
          CHECK_EQ(r->op(), less_than);
678 679 680 681 682 683
        }
      }
    }
  }
}

684
TEST(RemoveToNumberEffects) {
685 686
  JSTypedLoweringTester R;

687
  Node* effect_use = nullptr;
688
  Node* zero = R.graph.NewNode(R.common.NumberConstant(0));
689 690 691
  for (int i = 0; i < 10; i++) {
    Node* p0 = R.Parameter(Type::Number());
    Node* ton = R.Unop(R.javascript.ToNumber(), p0);
692
    Node* frame_state = R.EmptyFrameState(R.context());
693
    effect_use = nullptr;
694 695 696

    switch (i) {
      case 0:
697 698
        CHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(
                        R.javascript.ToNumber()));
699
        effect_use = R.graph.NewNode(R.javascript.ToNumber(), p0, R.context(),
700
                                     frame_state, ton, R.start());
701 702
        break;
      case 1:
703 704
        CHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(
                        R.javascript.ToNumber()));
705 706
        effect_use = R.graph.NewNode(R.javascript.ToNumber(), ton, R.context(),
                                     frame_state, ton, R.start());
707 708 709
        break;
      case 2:
        effect_use = R.graph.NewNode(R.common.EffectPhi(1), ton, R.start());
710
        break;
711
      case 3:
712
        effect_use = R.graph.NewNode(R.javascript.Add(R.binop_hints), ton, ton,
713
                                     R.context(), frame_state, ton, R.start());
714 715
        break;
      case 4:
716
        effect_use = R.graph.NewNode(R.javascript.Add(R.binop_hints), p0, p0,
717
                                     R.context(), frame_state, ton, R.start());
718 719
        break;
      case 5:
720 721
        effect_use =
            R.graph.NewNode(R.common.Return(), zero, p0, ton, R.start());
722 723
        break;
      case 6:
724 725
        effect_use =
            R.graph.NewNode(R.common.Return(), zero, ton, ton, R.start());
726 727 728
    }

    R.CheckEffectInput(R.start(), ton);
729
    if (effect_use != nullptr) R.CheckEffectInput(ton, effect_use);
730 731 732 733 734

    Node* r = R.reduce(ton);
    CHECK_EQ(p0, r);
    CHECK_NE(R.start(), r);

735
    if (effect_use != nullptr) {
736 737
      R.CheckEffectInput(R.start(), effect_use);
      // Check that value uses of ToNumber() do not go to start().
738
      for (int i = 0; i < effect_use->op()->ValueInputCount(); i++) {
739 740 741 742 743
        CHECK_NE(R.start(), effect_use->InputAt(i));
      }
    }
  }

744
  CHECK(!effect_use);  // should have done all cases above.
745 746 747 748 749 750
}


// Helper class for testing the reduction of a single binop.
class BinopEffectsTester {
 public:
751
  BinopEffectsTester(const Operator* op, Type t0, Type t1)
752
      : R(0),
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
        p0(R.Parameter(t0, 0)),
        p1(R.Parameter(t1, 1)),
        binop(R.Binop(op, p0, p1)),
        effect_use(R.graph.NewNode(R.common.EffectPhi(1), binop, R.start())) {
    // Effects should be ordered start -> binop -> effect_use
    R.CheckEffectInput(R.start(), binop);
    R.CheckEffectInput(binop, effect_use);
    result = R.reduce(binop);
  }

  JSTypedLoweringTester R;
  Node* p0;
  Node* p1;
  Node* binop;
  Node* effect_use;
  Node* result;

  void CheckEffectsRemoved() { R.CheckEffectInput(R.start(), effect_use); }

  void CheckEffectOrdering(Node* n0) {
    R.CheckEffectInput(R.start(), n0);
    R.CheckEffectInput(n0, effect_use);
  }

  void CheckEffectOrdering(Node* n0, Node* n1) {
    R.CheckEffectInput(R.start(), n0);
    R.CheckEffectInput(n0, n1);
    R.CheckEffectInput(n1, effect_use);
  }

  Node* CheckConvertedInput(IrOpcode::Value opcode, int which, bool effects) {
    return CheckConverted(opcode, result->InputAt(which), effects);
  }

  Node* CheckConverted(IrOpcode::Value opcode, Node* node, bool effects) {
    CHECK_EQ(opcode, node->opcode());
    if (effects) {
790
      CHECK_LT(0, node->op()->EffectInputCount());
791
    } else {
792
      CHECK_EQ(0, node->op()->EffectInputCount());
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
    }
    return node;
  }

  Node* CheckNoOp(int which) {
    CHECK_EQ(which == 0 ? p0 : p1, result->InputAt(which));
    return result->InputAt(which);
  }
};


// Helper function for strict and non-strict equality reductions.
void CheckEqualityReduction(JSTypedLoweringTester* R, bool strict, Node* l,
                            Node* r, IrOpcode::Value expected) {
  for (int j = 0; j < 2; j++) {
    Node* p0 = j == 0 ? l : r;
    Node* p1 = j == 1 ? l : r;

    {
812
      const Operator* op =
813 814
          strict ? R->javascript.StrictEqual(CompareOperationHint::kAny)
                 : R->javascript.Equal(CompareOperationHint::kAny);
815
      Node* eq = R->Binop(op, p0, p1);
816
      Node* r = R->reduce(eq);
817
      R->CheckBinop(expected, r);
818 819 820 821 822 823 824 825
    }
  }
}


TEST(EqualityForNumbers) {
  JSTypedLoweringTester R;

826 827 828
  Type simple_number_types[] = {Type::UnsignedSmall(), Type::SignedSmall(),
                                Type::Signed32(), Type::Unsigned32(),
                                Type::Number()};
829

830
  for (size_t i = 0; i < arraysize(simple_number_types); ++i) {
831 832
    Node* p0 = R.Parameter(simple_number_types[i], 0);

833
    for (size_t j = 0; j < arraysize(simple_number_types); ++j) {
834 835 836 837 838 839 840 841 842 843 844 845
      Node* p1 = R.Parameter(simple_number_types[j], 1);

      CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kNumberEqual);
      CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kNumberEqual);
    }
  }
}


TEST(StrictEqualityForRefEqualTypes) {
  JSTypedLoweringTester R;

846 847
  Type types[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
                  Type::Object(), Type::Receiver()};
848 849

  Node* p0 = R.Parameter(Type::Any());
850
  for (size_t i = 0; i < arraysize(types); i++) {
851 852 853 854 855
    Node* p1 = R.Parameter(types[i]);
    CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kReferenceEqual);
  }
}

856 857 858 859 860 861 862 863
TEST(StrictEqualityForUnique) {
  JSTypedLoweringTester R;

  Node* p0 = R.Parameter(Type::Unique());
  Node* p1 = R.Parameter(Type::Unique());
  CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kReferenceEqual);
  CheckEqualityReduction(&R, true, p1, p0, IrOpcode::kReferenceEqual);
}
864 865 866 867 868 869 870 871 872 873

TEST(StringEquality) {
  JSTypedLoweringTester R;
  Node* p0 = R.Parameter(Type::String());
  Node* p1 = R.Parameter(Type::String());

  CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kStringEqual);
  CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kStringEqual);
}

874
TEST(RemovePureNumberBinopEffects) {
875 876
  JSTypedLoweringTester R;

877
  const Operator* ops[] = {
878 879 880 881
      R.javascript.Equal(R.compare_hints),
      R.simplified.NumberEqual(),
      R.javascript.Add(R.binop_hints),
      R.simplified.NumberAdd(),
882
      R.javascript.Subtract(),
883
      R.simplified.NumberSubtract(),
884
      R.javascript.Multiply(),
885
      R.simplified.NumberMultiply(),
886
      R.javascript.Divide(),
887
      R.simplified.NumberDivide(),
888
      R.javascript.Modulus(),
889 890 891 892 893
      R.simplified.NumberModulus(),
      R.javascript.LessThan(R.compare_hints),
      R.simplified.NumberLessThan(),
      R.javascript.LessThanOrEqual(R.compare_hints),
      R.simplified.NumberLessThanOrEqual(),
894 895
  };

896
  for (size_t j = 0; j < arraysize(ops); j += 2) {
897 898 899
    BinopEffectsTester B(ops[j], Type::Number(), Type::Number());
    CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());

900
    B.R.CheckBinop(B.result->opcode(), B.result);
901 902 903 904 905 906 907 908 909

    B.CheckNoOp(0);
    B.CheckNoOp(1);

    B.CheckEffectsRemoved();
  }
}

TEST(Int32BinopEffects) {
910
  JSBitwiseTypedLoweringTester R;
911 912
  for (int j = 0; j < R.kNumberOps; j += 2) {
    bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
913
    BinopEffectsTester B(R.ops[j], I32Type(signed_left), I32Type(signed_right));
914 915
    CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());

916
    B.R.CheckBinop(B.result->opcode(), B.result);
917 918 919 920 921 922 923 924 925

    B.CheckNoOp(0);
    B.CheckNoOp(1);

    B.CheckEffectsRemoved();
  }

  for (int j = 0; j < R.kNumberOps; j += 2) {
    bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
926
    BinopEffectsTester B(R.ops[j], Type::Number(), Type::Number());
927 928
    CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());

929
    B.R.CheckBinop(B.result->opcode(), B.result);
930 931 932 933 934 935 936 937

    B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
    B.CheckConvertedInput(NumberToI32(signed_right), 1, false);

    B.CheckEffectsRemoved();
  }

  for (int j = 0; j < R.kNumberOps; j += 2) {
938 939
    bool signed_left = R.signedness[j];
    BinopEffectsTester B(R.ops[j], Type::Number(), Type::Boolean());
940

941
    B.R.CheckBinop(B.result->opcode(), B.result);
942

943 944
    B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
    B.CheckConvertedInput(IrOpcode::kPlainPrimitiveToNumber, 1, false);
945

946
    B.CheckEffectsRemoved();
947 948 949
  }

  for (int j = 0; j < R.kNumberOps; j += 2) {
950 951
    bool signed_right = R.signedness[j + 1];
    BinopEffectsTester B(R.ops[j], Type::Boolean(), Type::Number());
952

953
    B.R.CheckBinop(B.result->opcode(), B.result);
954

955 956
    B.CheckConvertedInput(IrOpcode::kPlainPrimitiveToNumber, 0, false);
    B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
957

958
    B.CheckEffectsRemoved();
959 960 961
  }

  for (int j = 0; j < R.kNumberOps; j += 2) {
962
    BinopEffectsTester B(R.ops[j], Type::Boolean(), Type::Boolean());
963

964
    B.R.CheckBinop(B.result->opcode(), B.result);
965

966 967
    B.CheckConvertedInput(IrOpcode::kPlainPrimitiveToNumber, 0, false);
    B.CheckConvertedInput(IrOpcode::kPlainPrimitiveToNumber, 1, false);
968

969
    B.CheckEffectsRemoved();
970 971 972
  }
}

973
TEST(Int32AddNarrowing) {
974
  {
975
    JSBitwiseTypedLoweringTester R;
976 977

    for (int o = 0; o < R.kNumberOps; o += 2) {
978
      for (size_t i = 0; i < arraysize(kInt32Types); i++) {
979
        Node* n0 = R.Parameter(kInt32Types[i]);
980
        for (size_t j = 0; j < arraysize(kInt32Types); j++) {
981 982 983 984 985 986 987 988 989 990
          Node* n1 = R.Parameter(kInt32Types[j]);
          Node* one = R.graph.NewNode(R.common.NumberConstant(1));

          for (int l = 0; l < 2; l++) {
            Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
            Node* or_node =
                R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
            Node* r = R.reduce(or_node);

            CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
991
            CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
992 993 994 995 996 997
          }
        }
      }
    }
  }
  {
998
    JSBitwiseShiftTypedLoweringTester R;
999 1000

    for (int o = 0; o < R.kNumberOps; o += 2) {
1001
      for (size_t i = 0; i < arraysize(kInt32Types); i++) {
1002
        Node* n0 = R.Parameter(kInt32Types[i]);
1003
        for (size_t j = 0; j < arraysize(kInt32Types); j++) {
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
          Node* n1 = R.Parameter(kInt32Types[j]);
          Node* one = R.graph.NewNode(R.common.NumberConstant(1));

          for (int l = 0; l < 2; l++) {
            Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
            Node* or_node =
                R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
            Node* r = R.reduce(or_node);

            CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1014
            CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
1015 1016 1017 1018 1019
          }
        }
      }
    }
  }
1020
  {
1021
    JSBitwiseTypedLoweringTester R;
1022

1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
    for (int o = 0; o < R.kNumberOps; o += 2) {
      Node* n0 = R.Parameter(I32Type(R.signedness[o]));
      Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
      Node* one = R.graph.NewNode(R.common.NumberConstant(1));

      Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
      Node* or_node = R.Binop(R.ops[o], add_node, one);
      Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
      Node* r = R.reduce(or_node);
      CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
      CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
      // Conversion to int32 should be done.
      CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
      CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
      // The other use should also not be touched.
      CHECK_EQ(add_node, other_use->InputAt(0));
      CHECK_EQ(one, other_use->InputAt(1));
    }
1041 1042 1043
  }
}

1044
TEST(Int32Comparisons) {
1045 1046 1047
  JSTypedLoweringTester R;

  struct Entry {
1048 1049
    const Operator* js_op;
    const Operator* num_op;
1050 1051 1052
    bool commute;
  };

1053 1054 1055 1056 1057 1058 1059 1060
  Entry ops[] = {{R.javascript.LessThan(R.compare_hints),
                  R.simplified.NumberLessThan(), false},
                 {R.javascript.LessThanOrEqual(R.compare_hints),
                  R.simplified.NumberLessThanOrEqual(), false},
                 {R.javascript.GreaterThan(R.compare_hints),
                  R.simplified.NumberLessThan(), true},
                 {R.javascript.GreaterThanOrEqual(R.compare_hints),
                  R.simplified.NumberLessThanOrEqual(), true}};
1061

1062 1063
  for (size_t o = 0; o < arraysize(ops); o++) {
    for (size_t i = 0; i < arraysize(kNumberTypes); i++) {
1064
      Type t0 = kNumberTypes[i];
1065 1066
      Node* p0 = R.Parameter(t0, 0);

1067
      for (size_t j = 0; j < arraysize(kNumberTypes); j++) {
1068
        Type t1 = kNumberTypes[j];
1069 1070 1071 1072 1073
        Node* p1 = R.Parameter(t1, 1);

        Node* cmp = R.Binop(ops[o].js_op, p0, p1);
        Node* r = R.reduce(cmp);

1074
        R.CheckBinop(ops[o].num_op, r);
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
        if (ops[o].commute) {
          CHECK_EQ(p1, r->InputAt(0));
          CHECK_EQ(p0, r->InputAt(1));
        } else {
          CHECK_EQ(p0, r->InputAt(0));
          CHECK_EQ(p1, r->InputAt(1));
        }
      }
    }
  }
}
1086 1087 1088 1089

}  // namespace compiler
}  // namespace internal
}  // namespace v8