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