1 // Copyright 2014 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/compilation-dependencies.h" 6 #include "src/compiler/js-graph.h" 7 #include "src/compiler/js-typed-lowering.h" 8 #include "src/compiler/machine-operator.h" 9 #include "src/compiler/node-properties.h" 10 #include "src/compiler/opcodes.h" 11 #include "src/compiler/operator-properties.h" 12 #include "src/compiler/simplified-operator.h" 13 #include "src/compiler/typer.h" 14 #include "test/cctest/cctest.h" 15 16 namespace v8 { 17 namespace internal { 18 namespace compiler { 19 20 #ifndef TEST_WITH_STRONG 21 #define TEST_WITH_STRONG(Name) \ 22 static void Test##Name(); \ 23 static void TestWithStrong##Name(LanguageMode language_mode); \ 24 CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, true, true); \ 25 static void Test##Name() { \ 26 TestWithStrong##Name(LanguageMode::SLOPPY); \ 27 TestWithStrong##Name(LanguageMode::STRONG); \ 28 } \ 29 static void TestWithStrong##Name(LanguageMode language_mode) 30 #endif 31 32 33 class JSTypedLoweringTester : public HandleAndZoneScope { 34 public: 35 explicit JSTypedLoweringTester(int num_parameters = 0) 36 : isolate(main_isolate()), 37 binop(NULL), 38 unop(NULL), 39 javascript(main_zone()), 40 machine(main_zone()), 41 simplified(main_zone()), 42 common(main_zone()), 43 deps(main_isolate(), main_zone()), 44 graph(main_zone()), 45 typer(main_isolate(), &graph), 46 context_node(NULL) { 47 graph.SetStart(graph.NewNode(common.Start(num_parameters))); 48 graph.SetEnd(graph.NewNode(common.End(1), graph.start())); 49 typer.Run(); 50 } 51 52 Isolate* isolate; 53 const Operator* binop; 54 const Operator* unop; 55 JSOperatorBuilder javascript; 56 MachineOperatorBuilder machine; 57 SimplifiedOperatorBuilder simplified; 58 CommonOperatorBuilder common; 59 CompilationDependencies deps; 60 Graph graph; 61 Typer typer; 62 Node* context_node; 63 BinaryOperationHints const hints = BinaryOperationHints::Any(); 64 65 Node* Parameter(Type* t, int32_t index = 0) { 66 Node* n = graph.NewNode(common.Parameter(index), graph.start()); 67 NodeProperties::SetType(n, t); 68 return n; 69 } 70 71 Node* UndefinedConstant() { 72 Handle<HeapObject> value = isolate->factory()->undefined_value(); 73 return graph.NewNode(common.HeapConstant(value)); 74 } 75 76 Node* HeapConstant(Handle<HeapObject> constant) { 77 return graph.NewNode(common.HeapConstant(constant)); 78 } 79 80 Node* EmptyFrameState(Node* context) { 81 Node* parameters = graph.NewNode(common.StateValues(0)); 82 Node* locals = graph.NewNode(common.StateValues(0)); 83 Node* stack = graph.NewNode(common.StateValues(0)); 84 85 Node* state_node = graph.NewNode( 86 common.FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(), 87 nullptr), 88 parameters, locals, stack, context, UndefinedConstant(), graph.start()); 89 90 return state_node; 91 } 92 93 Node* reduce(Node* node) { 94 JSGraph jsgraph(main_isolate(), &graph, &common, &javascript, &simplified, 95 &machine); 96 // TODO(titzer): mock the GraphReducer here for better unit testing. 97 GraphReducer graph_reducer(main_zone(), &graph); 98 JSTypedLowering reducer(&graph_reducer, &deps, 99 JSTypedLowering::kDeoptimizationEnabled, &jsgraph, 100 main_zone()); 101 Reduction reduction = reducer.Reduce(node); 102 if (reduction.Changed()) return reduction.replacement(); 103 return node; 104 } 105 106 Node* start() { return graph.start(); } 107 108 Node* context() { 109 if (context_node == NULL) { 110 context_node = graph.NewNode(common.Parameter(-1), graph.start()); 111 } 112 return context_node; 113 } 114 115 Node* control() { return start(); } 116 117 void CheckBinop(IrOpcode::Value expected, Node* node) { 118 CHECK_EQ(expected, node->opcode()); 119 } 120 121 void CheckBinop(const Operator* expected, Node* node) { 122 CHECK_EQ(expected->opcode(), node->op()->opcode()); 123 } 124 125 Node* ReduceUnop(const Operator* op, Type* input_type) { 126 return reduce(Unop(op, Parameter(input_type))); 127 } 128 129 Node* ReduceBinop(const Operator* op, Type* left_type, Type* right_type) { 130 return reduce(Binop(op, Parameter(left_type, 0), Parameter(right_type, 1))); 131 } 132 133 Node* Binop(const Operator* op, Node* left, Node* right) { 134 // JS binops also require context, effect, and control 135 std::vector<Node*> inputs; 136 inputs.push_back(left); 137 inputs.push_back(right); 138 if (OperatorProperties::HasContextInput(op)) { 139 inputs.push_back(context()); 140 } 141 for (int i = 0; i < OperatorProperties::GetFrameStateInputCount(op); i++) { 142 inputs.push_back(EmptyFrameState(context())); 143 } 144 if (op->EffectInputCount() > 0) { 145 inputs.push_back(start()); 146 } 147 if (op->ControlInputCount() > 0) { 148 inputs.push_back(control()); 149 } 150 return graph.NewNode(op, static_cast<int>(inputs.size()), 151 &(inputs.front())); 152 } 153 154 Node* Unop(const Operator* op, Node* input) { 155 // JS unops also require context, effect, and control 156 if (OperatorProperties::GetFrameStateInputCount(op) > 0) { 157 CHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(op)); 158 return graph.NewNode(op, input, context(), EmptyFrameState(context()), 159 start(), control()); 160 } else { 161 return graph.NewNode(op, input, context(), start(), control()); 162 } 163 } 164 165 Node* UseForEffect(Node* node) { 166 // TODO(titzer): use EffectPhi after fixing EffectCount 167 if (OperatorProperties::GetFrameStateInputCount(javascript.ToNumber()) > 168 0) { 169 CHECK_EQ(1, OperatorProperties::GetFrameStateInputCount( 170 javascript.ToNumber())); 171 return graph.NewNode(javascript.ToNumber(), node, context(), 172 EmptyFrameState(context()), node, control()); 173 } else { 174 return graph.NewNode(javascript.ToNumber(), node, context(), node, 175 control()); 176 } 177 } 178 179 void CheckEffectInput(Node* effect, Node* use) { 180 CHECK_EQ(effect, NodeProperties::GetEffectInput(use)); 181 } 182 183 void CheckInt32Constant(int32_t expected, Node* result) { 184 CHECK_EQ(IrOpcode::kInt32Constant, result->opcode()); 185 CHECK_EQ(expected, OpParameter<int32_t>(result)); 186 } 187 188 void CheckNumberConstant(double expected, Node* result) { 189 CHECK_EQ(IrOpcode::kNumberConstant, result->opcode()); 190 CHECK_EQ(expected, OpParameter<double>(result)); 191 } 192 193 void CheckNaN(Node* result) { 194 CHECK_EQ(IrOpcode::kNumberConstant, result->opcode()); 195 double value = OpParameter<double>(result); 196 CHECK(std::isnan(value)); 197 } 198 199 void CheckTrue(Node* result) { 200 CheckHandle(isolate->factory()->true_value(), result); 201 } 202 203 void CheckFalse(Node* result) { 204 CheckHandle(isolate->factory()->false_value(), result); 205 } 206 207 void CheckHandle(Handle<HeapObject> expected, Node* result) { 208 CHECK_EQ(IrOpcode::kHeapConstant, result->opcode()); 209 Handle<HeapObject> value = OpParameter<Handle<HeapObject>>(result); 210 CHECK_EQ(*expected, *value); 211 } 212 }; 213 214 static Type* kStringTypes[] = {Type::InternalizedString(), Type::OtherString(), 215 Type::String()}; 216 217 218 static Type* kInt32Types[] = {Type::UnsignedSmall(), Type::Negative32(), 219 Type::Unsigned31(), Type::SignedSmall(), 220 Type::Signed32(), Type::Unsigned32(), 221 Type::Integral32()}; 222 223 224 static Type* kNumberTypes[] = { 225 Type::UnsignedSmall(), Type::Negative32(), Type::Unsigned31(), 226 Type::SignedSmall(), Type::Signed32(), Type::Unsigned32(), 227 Type::Integral32(), Type::MinusZero(), Type::NaN(), 228 Type::OrderedNumber(), Type::PlainNumber(), Type::Number()}; 229 230 231 static Type* I32Type(bool is_signed) { 232 return is_signed ? Type::Signed32() : Type::Unsigned32(); 233 } 234 235 236 static IrOpcode::Value NumberToI32(bool is_signed) { 237 return is_signed ? IrOpcode::kNumberToInt32 : IrOpcode::kNumberToUint32; 238 } 239 240 241 // TODO(turbofan): Lowering of StringAdd is disabled for now. 242 #if 0 243 TEST_WITH_STRONG(StringBinops) { 244 JSTypedLoweringTester R; 245 246 for (size_t i = 0; i < arraysize(kStringTypes); ++i) { 247 Node* p0 = R.Parameter(kStringTypes[i], 0); 248 249 for (size_t j = 0; j < arraysize(kStringTypes); ++j) { 250 Node* p1 = R.Parameter(kStringTypes[j], 1); 251 252 Node* add = R.Binop(R.javascript.Add(language_mode), p0, p1); 253 Node* r = R.reduce(add); 254 255 R.CheckBinop(IrOpcode::kStringAdd, r); 256 CHECK_EQ(p0, r->InputAt(0)); 257 CHECK_EQ(p1, r->InputAt(1)); 258 } 259 } 260 } 261 #endif 262 263 264 TEST_WITH_STRONG(AddNumber1) { 265 JSTypedLoweringTester R; 266 for (size_t i = 0; i < arraysize(kNumberTypes); ++i) { 267 Node* p0 = R.Parameter(kNumberTypes[i], 0); 268 Node* p1 = R.Parameter(kNumberTypes[i], 1); 269 Node* add = R.Binop( 270 R.javascript.Add(language_mode, BinaryOperationHints::Any()), p0, p1); 271 Node* r = R.reduce(add); 272 273 R.CheckBinop(IrOpcode::kNumberAdd, r); 274 CHECK_EQ(p0, r->InputAt(0)); 275 CHECK_EQ(p1, r->InputAt(1)); 276 } 277 } 278 279 280 TEST_WITH_STRONG(NumberBinops) { 281 JSTypedLoweringTester R; 282 const Operator* ops[] = { 283 R.javascript.Add(language_mode, R.hints), 284 R.simplified.NumberAdd(), 285 R.javascript.Subtract(language_mode, R.hints), 286 R.simplified.NumberSubtract(), 287 R.javascript.Multiply(language_mode, R.hints), 288 R.simplified.NumberMultiply(), 289 R.javascript.Divide(language_mode, R.hints), 290 R.simplified.NumberDivide(), 291 R.javascript.Modulus(language_mode, R.hints), 292 R.simplified.NumberModulus(), 293 }; 294 295 for (size_t i = 0; i < arraysize(kNumberTypes); ++i) { 296 Node* p0 = R.Parameter(kNumberTypes[i], 0); 297 298 for (size_t j = 0; j < arraysize(kNumberTypes); ++j) { 299 Node* p1 = R.Parameter(kNumberTypes[j], 1); 300 301 for (size_t k = 0; k < arraysize(ops); k += 2) { 302 Node* add = R.Binop(ops[k], p0, p1); 303 Node* r = R.reduce(add); 304 305 R.CheckBinop(ops[k + 1], r); 306 CHECK_EQ(p0, r->InputAt(0)); 307 CHECK_EQ(p1, r->InputAt(1)); 308 } 309 } 310 } 311 } 312 313 314 static void CheckToI32(Node* old_input, Node* new_input, bool is_signed) { 315 Type* old_type = NodeProperties::GetType(old_input); 316 Type* new_type = NodeProperties::GetType(new_input); 317 Type* expected_type = I32Type(is_signed); 318 CHECK(new_type->Is(expected_type)); 319 if (old_type->Is(expected_type)) { 320 CHECK_EQ(old_input, new_input); 321 } else if (new_input->opcode() == IrOpcode::kNumberConstant) { 322 double v = OpParameter<double>(new_input); 323 double e = static_cast<double>(is_signed ? FastD2I(v) : FastD2UI(v)); 324 CHECK_EQ(e, v); 325 } 326 } 327 328 329 // A helper class for testing lowering of bitwise shift operators. 330 class JSBitwiseShiftTypedLoweringTester : public JSTypedLoweringTester { 331 public: 332 explicit JSBitwiseShiftTypedLoweringTester(LanguageMode language_mode) 333 : JSTypedLoweringTester(), language_mode_(language_mode) { 334 int i = 0; 335 set(i++, javascript.ShiftLeft(language_mode_, hints), true); 336 set(i++, simplified.NumberShiftLeft(), false); 337 set(i++, javascript.ShiftRight(language_mode_, hints), true); 338 set(i++, simplified.NumberShiftRight(), false); 339 set(i++, javascript.ShiftRightLogical(language_mode_, hints), false); 340 set(i++, simplified.NumberShiftRightLogical(), false); 341 } 342 static const int kNumberOps = 6; 343 const Operator* ops[kNumberOps]; 344 bool signedness[kNumberOps]; 345 346 private: 347 LanguageMode language_mode_; 348 void set(int idx, const Operator* op, bool s) { 349 ops[idx] = op; 350 signedness[idx] = s; 351 } 352 }; 353 354 355 TEST(Int32BitwiseShifts) { 356 JSBitwiseShiftTypedLoweringTester R(LanguageMode::SLOPPY); 357 358 Type* types[] = { 359 Type::SignedSmall(), Type::UnsignedSmall(), Type::Negative32(), 360 Type::Unsigned31(), Type::Unsigned32(), Type::Signed32(), 361 Type::MinusZero(), Type::NaN(), Type::Undefined(), 362 Type::Null(), Type::Boolean(), Type::Number(), 363 Type::PlainNumber(), Type::String()}; 364 365 for (size_t i = 0; i < arraysize(types); ++i) { 366 Node* p0 = R.Parameter(types[i], 0); 367 368 for (size_t j = 0; j < arraysize(types); ++j) { 369 Node* p1 = R.Parameter(types[j], 1); 370 371 for (int k = 0; k < R.kNumberOps; k += 2) { 372 Node* add = R.Binop(R.ops[k], p0, p1); 373 Node* r = R.reduce(add); 374 375 R.CheckBinop(R.ops[k + 1], r); 376 Node* r0 = r->InputAt(0); 377 Node* r1 = r->InputAt(1); 378 379 CheckToI32(p0, r0, R.signedness[k]); 380 CheckToI32(p1, r1, false); 381 } 382 } 383 } 384 } 385 386 387 // A helper class for testing lowering of bitwise operators. 388 class JSBitwiseTypedLoweringTester : public JSTypedLoweringTester { 389 public: 390 explicit JSBitwiseTypedLoweringTester(LanguageMode language_mode) 391 : JSTypedLoweringTester(), language_mode_(language_mode) { 392 int i = 0; 393 set(i++, javascript.BitwiseOr(language_mode_, hints), true); 394 set(i++, simplified.NumberBitwiseOr(), true); 395 set(i++, javascript.BitwiseXor(language_mode_, hints), true); 396 set(i++, simplified.NumberBitwiseXor(), true); 397 set(i++, javascript.BitwiseAnd(language_mode_, hints), true); 398 set(i++, simplified.NumberBitwiseAnd(), true); 399 } 400 static const int kNumberOps = 6; 401 const Operator* ops[kNumberOps]; 402 bool signedness[kNumberOps]; 403 404 private: 405 LanguageMode language_mode_; 406 void set(int idx, const Operator* op, bool s) { 407 ops[idx] = op; 408 signedness[idx] = s; 409 } 410 }; 411 412 413 TEST(Int32BitwiseBinops) { 414 JSBitwiseTypedLoweringTester R(LanguageMode::SLOPPY); 415 416 Type* types[] = { 417 Type::SignedSmall(), Type::UnsignedSmall(), Type::Unsigned32(), 418 Type::Signed32(), Type::MinusZero(), Type::NaN(), 419 Type::OrderedNumber(), Type::PlainNumber(), Type::Undefined(), 420 Type::Null(), Type::Boolean(), Type::Number(), 421 Type::String()}; 422 423 for (size_t i = 0; i < arraysize(types); ++i) { 424 Node* p0 = R.Parameter(types[i], 0); 425 426 for (size_t j = 0; j < arraysize(types); ++j) { 427 Node* p1 = R.Parameter(types[j], 1); 428 429 for (int k = 0; k < R.kNumberOps; k += 2) { 430 Node* add = R.Binop(R.ops[k], p0, p1); 431 Node* r = R.reduce(add); 432 433 R.CheckBinop(R.ops[k + 1], r); 434 435 CheckToI32(p0, r->InputAt(0), R.signedness[k]); 436 CheckToI32(p1, r->InputAt(1), R.signedness[k + 1]); 437 } 438 } 439 } 440 } 441 442 443 TEST(JSToNumber1) { 444 JSTypedLoweringTester R; 445 const Operator* ton = R.javascript.ToNumber(); 446 447 for (size_t i = 0; i < arraysize(kNumberTypes); i++) { // ToNumber(number) 448 Node* r = R.ReduceUnop(ton, kNumberTypes[i]); 449 CHECK_EQ(IrOpcode::kParameter, r->opcode()); 450 } 451 452 { // ToNumber(undefined) 453 Node* r = R.ReduceUnop(ton, Type::Undefined()); 454 R.CheckNaN(r); 455 } 456 457 { // ToNumber(null) 458 Node* r = R.ReduceUnop(ton, Type::Null()); 459 R.CheckNumberConstant(0.0, r); 460 } 461 } 462 463 464 TEST(JSToNumber_replacement) { 465 JSTypedLoweringTester R; 466 467 Type* types[] = {Type::Null(), Type::Undefined(), Type::Number()}; 468 469 for (size_t i = 0; i < arraysize(types); i++) { 470 Node* n = R.Parameter(types[i]); 471 Node* c = 472 R.graph.NewNode(R.javascript.ToNumber(), n, R.context(), 473 R.EmptyFrameState(R.context()), R.start(), R.start()); 474 Node* effect_use = R.UseForEffect(c); 475 Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c); 476 477 R.CheckEffectInput(c, effect_use); 478 Node* r = R.reduce(c); 479 480 if (types[i]->Is(Type::Number())) { 481 CHECK_EQ(n, r); 482 } else { 483 CHECK_EQ(IrOpcode::kNumberConstant, r->opcode()); 484 } 485 486 CHECK_EQ(n, add->InputAt(0)); 487 CHECK_EQ(r, add->InputAt(1)); 488 R.CheckEffectInput(R.start(), effect_use); 489 } 490 } 491 492 493 TEST(JSToNumberOfConstant) { 494 JSTypedLoweringTester R; 495 496 const Operator* ops[] = { 497 R.common.NumberConstant(0), R.common.NumberConstant(-1), 498 R.common.NumberConstant(0.1), R.common.Int32Constant(1177), 499 R.common.Float64Constant(0.99)}; 500 501 for (size_t i = 0; i < arraysize(ops); i++) { 502 Node* n = R.graph.NewNode(ops[i]); 503 Node* convert = R.Unop(R.javascript.ToNumber(), n); 504 Node* r = R.reduce(convert); 505 // Note that either outcome below is correct. It only depends on whether 506 // the types of constants are eagerly computed or only computed by the 507 // typing pass. 508 if (NodeProperties::GetType(n)->Is(Type::Number())) { 509 // If number constants are eagerly typed, then reduction should 510 // remove the ToNumber. 511 CHECK_EQ(n, r); 512 } else { 513 // Otherwise, type-based lowering should only look at the type, and 514 // *not* try to constant fold. 515 CHECK_EQ(convert, r); 516 } 517 } 518 } 519 520 521 TEST(JSToNumberOfNumberOrOtherPrimitive) { 522 JSTypedLoweringTester R; 523 Type* others[] = {Type::Undefined(), Type::Null(), Type::Boolean(), 524 Type::String()}; 525 526 for (size_t i = 0; i < arraysize(others); i++) { 527 Type* t = Type::Union(Type::Number(), others[i], R.main_zone()); 528 Node* r = R.ReduceUnop(R.javascript.ToNumber(), t); 529 CHECK_EQ(IrOpcode::kJSToNumber, r->opcode()); 530 } 531 } 532 533 534 TEST(JSToString1) { 535 JSTypedLoweringTester R; 536 537 for (size_t i = 0; i < arraysize(kStringTypes); i++) { 538 Node* r = R.ReduceUnop(R.javascript.ToString(), kStringTypes[i]); 539 CHECK_EQ(IrOpcode::kParameter, r->opcode()); 540 } 541 542 const Operator* op = R.javascript.ToString(); 543 544 { // ToString(undefined) => "undefined" 545 Node* r = R.ReduceUnop(op, Type::Undefined()); 546 R.CheckHandle(R.isolate->factory()->undefined_string(), r); 547 } 548 549 { // ToString(null) => "null" 550 Node* r = R.ReduceUnop(op, Type::Null()); 551 R.CheckHandle(R.isolate->factory()->null_string(), r); 552 } 553 554 { // ToString(boolean) 555 Node* r = R.ReduceUnop(op, Type::Boolean()); 556 CHECK_EQ(IrOpcode::kSelect, r->opcode()); 557 } 558 559 { // ToString(number) 560 Node* r = R.ReduceUnop(op, Type::Number()); 561 // TODO(titzer): could remove effects 562 CHECK_EQ(IrOpcode::kJSToString, r->opcode()); 563 } 564 565 { // ToString(string) 566 Node* r = R.ReduceUnop(op, Type::String()); 567 CHECK_EQ(IrOpcode::kParameter, r->opcode()); // No-op 568 } 569 570 { // ToString(object) 571 Node* r = R.ReduceUnop(op, Type::Object()); 572 CHECK_EQ(IrOpcode::kJSToString, r->opcode()); // No reduction. 573 } 574 } 575 576 577 TEST(JSToString_replacement) { 578 JSTypedLoweringTester R; 579 580 Type* types[] = {Type::Null(), Type::Undefined(), Type::String()}; 581 582 for (size_t i = 0; i < arraysize(types); i++) { 583 Node* n = R.Parameter(types[i]); 584 Node* c = 585 R.graph.NewNode(R.javascript.ToString(), n, R.context(), 586 R.EmptyFrameState(R.context()), R.start(), R.start()); 587 Node* effect_use = R.UseForEffect(c); 588 Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c); 589 590 R.CheckEffectInput(c, effect_use); 591 Node* r = R.reduce(c); 592 593 if (types[i]->Is(Type::String())) { 594 CHECK_EQ(n, r); 595 } else { 596 CHECK_EQ(IrOpcode::kHeapConstant, r->opcode()); 597 } 598 599 CHECK_EQ(n, add->InputAt(0)); 600 CHECK_EQ(r, add->InputAt(1)); 601 R.CheckEffectInput(R.start(), effect_use); 602 } 603 } 604 605 606 TEST_WITH_STRONG(StringComparison) { 607 JSTypedLoweringTester R; 608 609 const Operator* ops[] = { 610 R.javascript.LessThan(language_mode), R.simplified.StringLessThan(), 611 R.javascript.LessThanOrEqual(language_mode), 612 R.simplified.StringLessThanOrEqual(), 613 R.javascript.GreaterThan(language_mode), R.simplified.StringLessThan(), 614 R.javascript.GreaterThanOrEqual(language_mode), 615 R.simplified.StringLessThanOrEqual()}; 616 617 for (size_t i = 0; i < arraysize(kStringTypes); i++) { 618 Node* p0 = R.Parameter(kStringTypes[i], 0); 619 for (size_t j = 0; j < arraysize(kStringTypes); j++) { 620 Node* p1 = R.Parameter(kStringTypes[j], 1); 621 622 for (size_t k = 0; k < arraysize(ops); k += 2) { 623 Node* cmp = R.Binop(ops[k], p0, p1); 624 Node* r = R.reduce(cmp); 625 626 R.CheckBinop(ops[k + 1], r); 627 if (k >= 4) { 628 // GreaterThan and GreaterThanOrEqual commute the inputs 629 // and use the LessThan and LessThanOrEqual operators. 630 CHECK_EQ(p1, r->InputAt(0)); 631 CHECK_EQ(p0, r->InputAt(1)); 632 } else { 633 CHECK_EQ(p0, r->InputAt(0)); 634 CHECK_EQ(p1, r->InputAt(1)); 635 } 636 } 637 } 638 } 639 } 640 641 642 static void CheckIsConvertedToNumber(Node* val, Node* converted) { 643 if (NodeProperties::GetType(val)->Is(Type::Number())) { 644 CHECK_EQ(val, converted); 645 } else if (NodeProperties::GetType(val)->Is(Type::Boolean())) { 646 CHECK_EQ(IrOpcode::kBooleanToNumber, converted->opcode()); 647 CHECK_EQ(val, converted->InputAt(0)); 648 } else { 649 if (converted->opcode() == IrOpcode::kNumberConstant) return; 650 CHECK_EQ(IrOpcode::kJSToNumber, converted->opcode()); 651 CHECK_EQ(val, converted->InputAt(0)); 652 } 653 } 654 655 656 TEST_WITH_STRONG(NumberComparison) { 657 JSTypedLoweringTester R; 658 659 const Operator* ops[] = { 660 R.javascript.LessThan(language_mode), R.simplified.NumberLessThan(), 661 R.javascript.LessThanOrEqual(language_mode), 662 R.simplified.NumberLessThanOrEqual(), 663 R.javascript.GreaterThan(language_mode), R.simplified.NumberLessThan(), 664 R.javascript.GreaterThanOrEqual(language_mode), 665 R.simplified.NumberLessThanOrEqual()}; 666 667 Node* const p0 = R.Parameter(Type::Number(), 0); 668 Node* const p1 = R.Parameter(Type::Number(), 1); 669 670 for (size_t k = 0; k < arraysize(ops); k += 2) { 671 Node* cmp = R.Binop(ops[k], p0, p1); 672 Node* r = R.reduce(cmp); 673 674 R.CheckBinop(ops[k + 1], r); 675 if (k >= 4) { 676 // GreaterThan and GreaterThanOrEqual commute the inputs 677 // and use the LessThan and LessThanOrEqual operators. 678 CheckIsConvertedToNumber(p1, r->InputAt(0)); 679 CheckIsConvertedToNumber(p0, r->InputAt(1)); 680 } else { 681 CheckIsConvertedToNumber(p0, r->InputAt(0)); 682 CheckIsConvertedToNumber(p1, r->InputAt(1)); 683 } 684 } 685 } 686 687 688 TEST_WITH_STRONG(MixedComparison1) { 689 JSTypedLoweringTester R; 690 691 Type* types[] = {Type::Number(), Type::String(), 692 Type::Union(Type::Number(), Type::String(), R.main_zone())}; 693 694 for (size_t i = 0; i < arraysize(types); i++) { 695 Node* p0 = R.Parameter(types[i], 0); 696 697 for (size_t j = 0; j < arraysize(types); j++) { 698 Node* p1 = R.Parameter(types[j], 1); 699 { 700 const Operator* less_than = R.javascript.LessThan(language_mode); 701 Node* cmp = R.Binop(less_than, p0, p1); 702 Node* r = R.reduce(cmp); 703 if (types[i]->Is(Type::String()) && types[j]->Is(Type::String())) { 704 R.CheckBinop(R.simplified.StringLessThan(), r); 705 } else if ((types[i]->Is(Type::Number()) && 706 types[j]->Is(Type::Number())) || 707 (!is_strong(language_mode) && 708 (!types[i]->Maybe(Type::String()) || 709 !types[j]->Maybe(Type::String())))) { 710 R.CheckBinop(R.simplified.NumberLessThan(), r); 711 } else { 712 // No reduction of mixed types. 713 CHECK_EQ(r->op(), less_than); 714 } 715 } 716 } 717 } 718 } 719 720 721 TEST_WITH_STRONG(RemoveToNumberEffects) { 722 JSTypedLoweringTester R; 723 724 Node* effect_use = NULL; 725 for (int i = 0; i < 10; i++) { 726 Node* p0 = R.Parameter(Type::Number()); 727 Node* ton = R.Unop(R.javascript.ToNumber(), p0); 728 Node* frame_state = R.EmptyFrameState(R.context()); 729 effect_use = NULL; 730 731 switch (i) { 732 case 0: 733 CHECK_EQ(1, OperatorProperties::GetFrameStateInputCount( 734 R.javascript.ToNumber())); 735 effect_use = R.graph.NewNode(R.javascript.ToNumber(), p0, R.context(), 736 frame_state, ton, R.start()); 737 break; 738 case 1: 739 CHECK_EQ(1, OperatorProperties::GetFrameStateInputCount( 740 R.javascript.ToNumber())); 741 effect_use = R.graph.NewNode(R.javascript.ToNumber(), ton, R.context(), 742 frame_state, ton, R.start()); 743 break; 744 case 2: 745 effect_use = R.graph.NewNode(R.common.EffectPhi(1), ton, R.start()); 746 case 3: 747 effect_use = R.graph.NewNode(R.javascript.Add(language_mode, R.hints), 748 ton, ton, R.context(), frame_state, 749 frame_state, ton, R.start()); 750 break; 751 case 4: 752 effect_use = R.graph.NewNode(R.javascript.Add(language_mode, R.hints), 753 p0, p0, R.context(), frame_state, 754 frame_state, ton, R.start()); 755 break; 756 case 5: 757 effect_use = R.graph.NewNode(R.common.Return(), p0, ton, R.start()); 758 break; 759 case 6: 760 effect_use = R.graph.NewNode(R.common.Return(), ton, ton, R.start()); 761 } 762 763 R.CheckEffectInput(R.start(), ton); 764 if (effect_use != NULL) R.CheckEffectInput(ton, effect_use); 765 766 Node* r = R.reduce(ton); 767 CHECK_EQ(p0, r); 768 CHECK_NE(R.start(), r); 769 770 if (effect_use != NULL) { 771 R.CheckEffectInput(R.start(), effect_use); 772 // Check that value uses of ToNumber() do not go to start(). 773 for (int i = 0; i < effect_use->op()->ValueInputCount(); i++) { 774 CHECK_NE(R.start(), effect_use->InputAt(i)); 775 } 776 } 777 } 778 779 CHECK(!effect_use); // should have done all cases above. 780 } 781 782 783 // Helper class for testing the reduction of a single binop. 784 class BinopEffectsTester { 785 public: 786 explicit BinopEffectsTester(const Operator* op, Type* t0, Type* t1) 787 : R(), 788 p0(R.Parameter(t0, 0)), 789 p1(R.Parameter(t1, 1)), 790 binop(R.Binop(op, p0, p1)), 791 effect_use(R.graph.NewNode(R.common.EffectPhi(1), binop, R.start())) { 792 // Effects should be ordered start -> binop -> effect_use 793 R.CheckEffectInput(R.start(), binop); 794 R.CheckEffectInput(binop, effect_use); 795 result = R.reduce(binop); 796 } 797 798 JSTypedLoweringTester R; 799 Node* p0; 800 Node* p1; 801 Node* binop; 802 Node* effect_use; 803 Node* result; 804 805 void CheckEffectsRemoved() { R.CheckEffectInput(R.start(), effect_use); } 806 807 void CheckEffectOrdering(Node* n0) { 808 R.CheckEffectInput(R.start(), n0); 809 R.CheckEffectInput(n0, effect_use); 810 } 811 812 void CheckEffectOrdering(Node* n0, Node* n1) { 813 R.CheckEffectInput(R.start(), n0); 814 R.CheckEffectInput(n0, n1); 815 R.CheckEffectInput(n1, effect_use); 816 } 817 818 Node* CheckConvertedInput(IrOpcode::Value opcode, int which, bool effects) { 819 return CheckConverted(opcode, result->InputAt(which), effects); 820 } 821 822 Node* CheckConverted(IrOpcode::Value opcode, Node* node, bool effects) { 823 CHECK_EQ(opcode, node->opcode()); 824 if (effects) { 825 CHECK_LT(0, node->op()->EffectInputCount()); 826 } else { 827 CHECK_EQ(0, node->op()->EffectInputCount()); 828 } 829 return node; 830 } 831 832 Node* CheckNoOp(int which) { 833 CHECK_EQ(which == 0 ? p0 : p1, result->InputAt(which)); 834 return result->InputAt(which); 835 } 836 }; 837 838 839 // Helper function for strict and non-strict equality reductions. 840 void CheckEqualityReduction(JSTypedLoweringTester* R, bool strict, Node* l, 841 Node* r, IrOpcode::Value expected) { 842 for (int j = 0; j < 2; j++) { 843 Node* p0 = j == 0 ? l : r; 844 Node* p1 = j == 1 ? l : r; 845 846 { 847 const Operator* op = 848 strict ? R->javascript.StrictEqual() : R->javascript.Equal(); 849 Node* eq = R->Binop(op, p0, p1); 850 Node* r = R->reduce(eq); 851 R->CheckBinop(expected, r); 852 } 853 854 { 855 const Operator* op = 856 strict ? R->javascript.StrictNotEqual() : R->javascript.NotEqual(); 857 Node* ne = R->Binop(op, p0, p1); 858 Node* n = R->reduce(ne); 859 CHECK_EQ(IrOpcode::kBooleanNot, n->opcode()); 860 Node* r = n->InputAt(0); 861 R->CheckBinop(expected, r); 862 } 863 } 864 } 865 866 867 TEST(EqualityForNumbers) { 868 JSTypedLoweringTester R; 869 870 Type* simple_number_types[] = {Type::UnsignedSmall(), Type::SignedSmall(), 871 Type::Signed32(), Type::Unsigned32(), 872 Type::Number()}; 873 874 875 for (size_t i = 0; i < arraysize(simple_number_types); ++i) { 876 Node* p0 = R.Parameter(simple_number_types[i], 0); 877 878 for (size_t j = 0; j < arraysize(simple_number_types); ++j) { 879 Node* p1 = R.Parameter(simple_number_types[j], 1); 880 881 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kNumberEqual); 882 CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kNumberEqual); 883 } 884 } 885 } 886 887 888 TEST(StrictEqualityForRefEqualTypes) { 889 JSTypedLoweringTester R; 890 891 Type* types[] = {Type::Undefined(), Type::Null(), Type::Boolean(), 892 Type::Object(), Type::Receiver()}; 893 894 Node* p0 = R.Parameter(Type::Any()); 895 for (size_t i = 0; i < arraysize(types); i++) { 896 Node* p1 = R.Parameter(types[i]); 897 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kReferenceEqual); 898 } 899 // TODO(titzer): Equal(RefEqualTypes) 900 } 901 902 903 TEST(StringEquality) { 904 JSTypedLoweringTester R; 905 Node* p0 = R.Parameter(Type::String()); 906 Node* p1 = R.Parameter(Type::String()); 907 908 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kStringEqual); 909 CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kStringEqual); 910 } 911 912 913 TEST_WITH_STRONG(RemovePureNumberBinopEffects) { 914 JSTypedLoweringTester R; 915 916 const Operator* ops[] = { 917 R.javascript.Equal(), 918 R.simplified.NumberEqual(), 919 R.javascript.Add(language_mode, R.hints), 920 R.simplified.NumberAdd(), 921 R.javascript.Subtract(language_mode, R.hints), 922 R.simplified.NumberSubtract(), 923 R.javascript.Multiply(language_mode, R.hints), 924 R.simplified.NumberMultiply(), 925 R.javascript.Divide(language_mode, R.hints), 926 R.simplified.NumberDivide(), 927 R.javascript.Modulus(language_mode, R.hints), 928 R.simplified.NumberModulus(), 929 R.javascript.LessThan(language_mode), 930 R.simplified.NumberLessThan(), 931 R.javascript.LessThanOrEqual(language_mode), 932 R.simplified.NumberLessThanOrEqual(), 933 }; 934 935 for (size_t j = 0; j < arraysize(ops); j += 2) { 936 BinopEffectsTester B(ops[j], Type::Number(), Type::Number()); 937 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode()); 938 939 B.R.CheckBinop(B.result->opcode(), B.result); 940 941 B.CheckNoOp(0); 942 B.CheckNoOp(1); 943 944 B.CheckEffectsRemoved(); 945 } 946 } 947 948 949 TEST(OrderNumberBinopEffects1) { 950 JSTypedLoweringTester R; 951 952 const Operator* ops[] = { 953 R.javascript.Subtract(LanguageMode::SLOPPY, R.hints), 954 R.simplified.NumberSubtract(), 955 R.javascript.Multiply(LanguageMode::SLOPPY, R.hints), 956 R.simplified.NumberMultiply(), 957 R.javascript.Divide(LanguageMode::SLOPPY, R.hints), 958 R.simplified.NumberDivide(), 959 }; 960 961 for (size_t j = 0; j < arraysize(ops); j += 2) { 962 BinopEffectsTester B(ops[j], Type::Symbol(), Type::Symbol()); 963 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode()); 964 965 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true); 966 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true); 967 968 CHECK_EQ(B.p0, i0->InputAt(0)); 969 CHECK_EQ(B.p1, i1->InputAt(0)); 970 971 // Effects should be ordered start -> i0 -> i1 -> effect_use 972 B.CheckEffectOrdering(i0, i1); 973 } 974 } 975 976 977 TEST(OrderNumberBinopEffects2) { 978 JSTypedLoweringTester R; 979 980 const Operator* ops[] = { 981 R.javascript.Add(LanguageMode::SLOPPY, R.hints), 982 R.simplified.NumberAdd(), 983 R.javascript.Subtract(LanguageMode::SLOPPY, R.hints), 984 R.simplified.NumberSubtract(), 985 R.javascript.Multiply(LanguageMode::SLOPPY, R.hints), 986 R.simplified.NumberMultiply(), 987 R.javascript.Divide(LanguageMode::SLOPPY, R.hints), 988 R.simplified.NumberDivide(), 989 }; 990 991 for (size_t j = 0; j < arraysize(ops); j += 2) { 992 BinopEffectsTester B(ops[j], Type::Number(), Type::Symbol()); 993 994 Node* i0 = B.CheckNoOp(0); 995 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true); 996 997 CHECK_EQ(B.p0, i0); 998 CHECK_EQ(B.p1, i1->InputAt(0)); 999 1000 // Effects should be ordered start -> i1 -> effect_use 1001 B.CheckEffectOrdering(i1); 1002 } 1003 1004 for (size_t j = 0; j < arraysize(ops); j += 2) { 1005 BinopEffectsTester B(ops[j], Type::Symbol(), Type::Number()); 1006 1007 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true); 1008 Node* i1 = B.CheckNoOp(1); 1009 1010 CHECK_EQ(B.p0, i0->InputAt(0)); 1011 CHECK_EQ(B.p1, i1); 1012 1013 // Effects should be ordered start -> i0 -> effect_use 1014 B.CheckEffectOrdering(i0); 1015 } 1016 } 1017 1018 1019 TEST(OrderCompareEffects) { 1020 JSTypedLoweringTester R; 1021 1022 const Operator* ops[] = { 1023 R.javascript.GreaterThan(LanguageMode::SLOPPY), 1024 R.simplified.NumberLessThan(), 1025 R.javascript.GreaterThanOrEqual(LanguageMode::SLOPPY), 1026 R.simplified.NumberLessThanOrEqual(), 1027 }; 1028 1029 for (size_t j = 0; j < arraysize(ops); j += 2) { 1030 BinopEffectsTester B(ops[j], Type::Symbol(), Type::String()); 1031 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode()); 1032 1033 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true); 1034 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true); 1035 1036 // Inputs should be commuted. 1037 CHECK_EQ(B.p1, i0->InputAt(0)); 1038 CHECK_EQ(B.p0, i1->InputAt(0)); 1039 1040 // But effects should be ordered start -> i1 -> effect_use 1041 B.CheckEffectOrdering(i1); 1042 } 1043 1044 for (size_t j = 0; j < arraysize(ops); j += 2) { 1045 BinopEffectsTester B(ops[j], Type::Number(), Type::Symbol()); 1046 1047 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true); 1048 Node* i1 = B.result->InputAt(1); 1049 1050 CHECK_EQ(B.p1, i0->InputAt(0)); // Should be commuted. 1051 CHECK_EQ(B.p0, i1); 1052 1053 // Effects should be ordered start -> i1 -> effect_use 1054 B.CheckEffectOrdering(i0); 1055 } 1056 1057 for (size_t j = 0; j < arraysize(ops); j += 2) { 1058 BinopEffectsTester B(ops[j], Type::Symbol(), Type::Number()); 1059 1060 Node* i0 = B.result->InputAt(0); 1061 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true); 1062 1063 CHECK_EQ(B.p1, i0); // Should be commuted. 1064 CHECK_EQ(B.p0, i1->InputAt(0)); 1065 1066 // Effects should be ordered start -> i0 -> effect_use 1067 B.CheckEffectOrdering(i1); 1068 } 1069 } 1070 1071 1072 TEST(Int32BinopEffects) { 1073 JSBitwiseTypedLoweringTester R(LanguageMode::SLOPPY); 1074 for (int j = 0; j < R.kNumberOps; j += 2) { 1075 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1]; 1076 BinopEffectsTester B(R.ops[j], I32Type(signed_left), I32Type(signed_right)); 1077 CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode()); 1078 1079 B.R.CheckBinop(B.result->opcode(), B.result); 1080 1081 B.CheckNoOp(0); 1082 B.CheckNoOp(1); 1083 1084 B.CheckEffectsRemoved(); 1085 } 1086 1087 for (int j = 0; j < R.kNumberOps; j += 2) { 1088 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1]; 1089 BinopEffectsTester B(R.ops[j], Type::Number(), Type::Number()); 1090 CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode()); 1091 1092 B.R.CheckBinop(B.result->opcode(), B.result); 1093 1094 B.CheckConvertedInput(NumberToI32(signed_left), 0, false); 1095 B.CheckConvertedInput(NumberToI32(signed_right), 1, false); 1096 1097 B.CheckEffectsRemoved(); 1098 } 1099 1100 for (int j = 0; j < R.kNumberOps; j += 2) { 1101 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1]; 1102 BinopEffectsTester B(R.ops[j], Type::Number(), Type::Primitive()); 1103 1104 B.R.CheckBinop(B.result->opcode(), B.result); 1105 1106 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false); 1107 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false); 1108 1109 CHECK_EQ(B.p0, i0->InputAt(0)); 1110 Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true); 1111 1112 CHECK_EQ(B.p1, ii1->InputAt(0)); 1113 1114 B.CheckEffectOrdering(ii1); 1115 } 1116 1117 for (int j = 0; j < R.kNumberOps; j += 2) { 1118 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1]; 1119 BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Number()); 1120 1121 B.R.CheckBinop(B.result->opcode(), B.result); 1122 1123 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false); 1124 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false); 1125 1126 Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true); 1127 CHECK_EQ(B.p1, i1->InputAt(0)); 1128 1129 CHECK_EQ(B.p0, ii0->InputAt(0)); 1130 1131 B.CheckEffectOrdering(ii0); 1132 } 1133 1134 for (int j = 0; j < R.kNumberOps; j += 2) { 1135 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1]; 1136 BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Primitive()); 1137 1138 B.R.CheckBinop(B.result->opcode(), B.result); 1139 1140 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false); 1141 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false); 1142 1143 Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true); 1144 Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true); 1145 1146 CHECK_EQ(B.p0, ii0->InputAt(0)); 1147 CHECK_EQ(B.p1, ii1->InputAt(0)); 1148 1149 B.CheckEffectOrdering(ii0, ii1); 1150 } 1151 } 1152 1153 1154 TEST_WITH_STRONG(Int32AddNarrowing) { 1155 { 1156 JSBitwiseTypedLoweringTester R(language_mode); 1157 1158 for (int o = 0; o < R.kNumberOps; o += 2) { 1159 for (size_t i = 0; i < arraysize(kInt32Types); i++) { 1160 Node* n0 = R.Parameter(kInt32Types[i]); 1161 for (size_t j = 0; j < arraysize(kInt32Types); j++) { 1162 Node* n1 = R.Parameter(kInt32Types[j]); 1163 Node* one = R.graph.NewNode(R.common.NumberConstant(1)); 1164 1165 for (int l = 0; l < 2; l++) { 1166 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1); 1167 Node* or_node = 1168 R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node); 1169 Node* r = R.reduce(or_node); 1170 1171 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode()); 1172 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode()); 1173 } 1174 } 1175 } 1176 } 1177 } 1178 { 1179 JSBitwiseShiftTypedLoweringTester R(language_mode); 1180 1181 for (int o = 0; o < R.kNumberOps; o += 2) { 1182 for (size_t i = 0; i < arraysize(kInt32Types); i++) { 1183 Node* n0 = R.Parameter(kInt32Types[i]); 1184 for (size_t j = 0; j < arraysize(kInt32Types); j++) { 1185 Node* n1 = R.Parameter(kInt32Types[j]); 1186 Node* one = R.graph.NewNode(R.common.NumberConstant(1)); 1187 1188 for (int l = 0; l < 2; l++) { 1189 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1); 1190 Node* or_node = 1191 R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node); 1192 Node* r = R.reduce(or_node); 1193 1194 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode()); 1195 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode()); 1196 } 1197 } 1198 } 1199 } 1200 } 1201 { 1202 JSBitwiseTypedLoweringTester R(language_mode); 1203 1204 for (int o = 0; o < R.kNumberOps; o += 2) { 1205 Node* n0 = R.Parameter(I32Type(R.signedness[o])); 1206 Node* n1 = R.Parameter(I32Type(R.signedness[o + 1])); 1207 Node* one = R.graph.NewNode(R.common.NumberConstant(1)); 1208 1209 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1); 1210 Node* or_node = R.Binop(R.ops[o], add_node, one); 1211 Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one); 1212 Node* r = R.reduce(or_node); 1213 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode()); 1214 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode()); 1215 // Conversion to int32 should be done. 1216 CheckToI32(add_node, r->InputAt(0), R.signedness[o]); 1217 CheckToI32(one, r->InputAt(1), R.signedness[o + 1]); 1218 // The other use should also not be touched. 1219 CHECK_EQ(add_node, other_use->InputAt(0)); 1220 CHECK_EQ(one, other_use->InputAt(1)); 1221 } 1222 } 1223 } 1224 1225 1226 TEST_WITH_STRONG(Int32Comparisons) { 1227 JSTypedLoweringTester R; 1228 1229 struct Entry { 1230 const Operator* js_op; 1231 const Operator* uint_op; 1232 const Operator* int_op; 1233 const Operator* num_op; 1234 bool commute; 1235 }; 1236 1237 Entry ops[] = { 1238 {R.javascript.LessThan(language_mode), R.machine.Uint32LessThan(), 1239 R.machine.Int32LessThan(), R.simplified.NumberLessThan(), false}, 1240 {R.javascript.LessThanOrEqual(language_mode), 1241 R.machine.Uint32LessThanOrEqual(), R.machine.Int32LessThanOrEqual(), 1242 R.simplified.NumberLessThanOrEqual(), false}, 1243 {R.javascript.GreaterThan(language_mode), R.machine.Uint32LessThan(), 1244 R.machine.Int32LessThan(), R.simplified.NumberLessThan(), true}, 1245 {R.javascript.GreaterThanOrEqual(language_mode), 1246 R.machine.Uint32LessThanOrEqual(), R.machine.Int32LessThanOrEqual(), 1247 R.simplified.NumberLessThanOrEqual(), true} 1248 }; 1249 1250 for (size_t o = 0; o < arraysize(ops); o++) { 1251 for (size_t i = 0; i < arraysize(kNumberTypes); i++) { 1252 Type* t0 = kNumberTypes[i]; 1253 Node* p0 = R.Parameter(t0, 0); 1254 1255 for (size_t j = 0; j < arraysize(kNumberTypes); j++) { 1256 Type* t1 = kNumberTypes[j]; 1257 Node* p1 = R.Parameter(t1, 1); 1258 1259 Node* cmp = R.Binop(ops[o].js_op, p0, p1); 1260 Node* r = R.reduce(cmp); 1261 1262 const Operator* expected; 1263 if (t0->Is(Type::Unsigned32()) && t1->Is(Type::Unsigned32())) { 1264 expected = ops[o].uint_op; 1265 } else if (t0->Is(Type::Signed32()) && t1->Is(Type::Signed32())) { 1266 expected = ops[o].int_op; 1267 } else { 1268 expected = ops[o].num_op; 1269 } 1270 R.CheckBinop(expected, r); 1271 if (ops[o].commute) { 1272 CHECK_EQ(p1, r->InputAt(0)); 1273 CHECK_EQ(p0, r->InputAt(1)); 1274 } else { 1275 CHECK_EQ(p0, r->InputAt(0)); 1276 CHECK_EQ(p1, r->InputAt(1)); 1277 } 1278 } 1279 } 1280 } 1281 } 1282 1283 } // namespace compiler 1284 } // namespace internal 1285 } // namespace v8 1286