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:
JSTypedLoweringTester(int num_parameters=0)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
Parameter(Type * t,int32_t index=0)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
UndefinedConstant()51 Node* UndefinedConstant() {
52 Unique<Object> unique =
53 Unique<Object>::CreateImmovable(isolate->factory()->undefined_value());
54 return graph.NewNode(common.HeapConstant(unique));
55 }
56
HeapConstant(Handle<Object> constant)57 Node* HeapConstant(Handle<Object> constant) {
58 Unique<Object> unique = Unique<Object>::CreateUninitialized(constant);
59 return graph.NewNode(common.HeapConstant(unique));
60 }
61
EmptyFrameState(Node * context)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
reduce(Node * node)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
start()82 Node* start() { return graph.start(); }
83
context()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
control()91 Node* control() { return start(); }
92
CheckPureBinop(IrOpcode::Value expected,Node * node)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
CheckPureBinop(const Operator * expected,Node * node)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
ReduceUnop(const Operator * op,Type * input_type)103 Node* ReduceUnop(const Operator* op, Type* input_type) {
104 return reduce(Unop(op, Parameter(input_type)));
105 }
106
ReduceBinop(const Operator * op,Type * left_type,Type * right_type)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
Binop(const Operator * op,Node * left,Node * right)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
Unop(const Operator * op,Node * input)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
UseForEffect(Node * node)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
CheckEffectInput(Node * effect,Node * use)127 void CheckEffectInput(Node* effect, Node* use) {
128 CHECK_EQ(effect, NodeProperties::GetEffectInput(use));
129 }
130
CheckInt32Constant(int32_t expected,Node * result)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
CheckNumberConstant(double expected,Node * result)136 void CheckNumberConstant(double expected, Node* result) {
137 CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
138 CHECK_EQ(expected, OpParameter<double>(result));
139 }
140
CheckNaN(Node * result)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
CheckTrue(Node * result)147 void CheckTrue(Node* result) {
148 CheckHandle(isolate->factory()->true_value(), result);
149 }
150
CheckFalse(Node * result)151 void CheckFalse(Node* result) {
152 CheckHandle(isolate->factory()->false_value(), result);
153 }
154
CheckHandle(Handle<Object> expected,Node * result)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
I32Type(bool is_signed)184 static Type* I32Type(bool is_signed) {
185 return is_signed ? Type::Signed32() : Type::Unsigned32();
186 }
187
188
NumberToI32(bool is_signed)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
TEST(AddNumber1)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
TEST(NumberBinops)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
CheckToI32(Node * old_input,Node * new_input,bool is_signed)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
JSBitwiseShiftTypedLoweringTester()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:
set(int idx,const Operator * op,bool s)295 void set(int idx, const Operator* op, bool s) {
296 ops[idx] = op;
297 signedness[idx] = s;
298 }
299 };
300
301
TEST(Int32BitwiseShifts)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
JSBitwiseTypedLoweringTester()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:
set(int idx,const Operator * op,bool s)355 void set(int idx, const Operator* op, bool s) {
356 ops[idx] = op;
357 signedness[idx] = s;
358 }
359 };
360
361
TEST(Int32BitwiseBinops)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
TEST(JSToNumber1)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
TEST(JSToNumber_replacement)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
TEST(JSToNumberOfConstant)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
TEST(JSToNumberOfNumberOrOtherPrimitive)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
TEST(JSToBoolean)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
TEST(JSToBoolean_replacement)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
TEST(JSToString1)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
TEST(JSToString_replacement)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
TEST(StringComparison)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
CheckIsConvertedToNumber(Node * val,Node * converted)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
TEST(NumberComparison)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
TEST(MixedComparison1)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
TEST(ObjectComparison)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
TEST(UnaryNot)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
TEST(RemoveToNumberEffects)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:
BinopEffectsTester(const Operator * op,Type * t0,Type * t1)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
CheckEffectsRemoved()887 void CheckEffectsRemoved() { R.CheckEffectInput(R.start(), effect_use); }
888
CheckEffectOrdering(Node * n0)889 void CheckEffectOrdering(Node* n0) {
890 R.CheckEffectInput(R.start(), n0);
891 R.CheckEffectInput(n0, effect_use);
892 }
893
CheckEffectOrdering(Node * n0,Node * n1)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
CheckConvertedInput(IrOpcode::Value opcode,int which,bool effects)900 Node* CheckConvertedInput(IrOpcode::Value opcode, int which, bool effects) {
901 return CheckConverted(opcode, result->InputAt(which), effects);
902 }
903
CheckConverted(IrOpcode::Value opcode,Node * node,bool effects)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
CheckNoOp(int which)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.
CheckEqualityReduction(JSTypedLoweringTester * R,bool strict,Node * l,Node * r,IrOpcode::Value expected)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
TEST(EqualityForNumbers)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
TEST(StrictEqualityForRefEqualTypes)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
TEST(StringEquality)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
TEST(RemovePureNumberBinopEffects)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
TEST(OrderNumberBinopEffects1)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
TEST(OrderNumberBinopEffects2)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
TEST(OrderCompareEffects)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
TEST(Int32BinopEffects)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
TEST(UnaryNotEffects)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
TEST(Int32AddNarrowing)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
TEST(Int32AddNarrowingNotOwned)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
TEST(Int32Comparisons)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