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.
8 #include "test/cctest/cctest.h"
9 #include "test/cctest/compiler/graph-builder-tester.h"
10 #include "test/cctest/compiler/value-helper.h"
12 #include "src/compiler/node-matchers.h"
13 #include "src/compiler/representation-change.h"
15 using namespace v8::internal;
16 using namespace v8::internal::compiler;
18 namespace v8 { // for friendiness.
22 class RepresentationChangerTester : public HandleAndZoneScope,
23 public GraphAndBuilders {
25 explicit RepresentationChangerTester(int num_parameters = 0)
26 : GraphAndBuilders(main_zone()),
27 javascript_(main_zone()),
28 jsgraph_(main_graph_, &main_common_, &javascript_, &main_machine_),
29 changer_(&jsgraph_, &main_simplified_, main_isolate()) {
30 Node* s = graph()->NewNode(common()->Start(num_parameters));
34 JSOperatorBuilder javascript_;
36 RepresentationChanger changer_;
38 Isolate* isolate() { return main_isolate(); }
39 Graph* graph() { return main_graph_; }
40 CommonOperatorBuilder* common() { return &main_common_; }
41 JSGraph* jsgraph() { return &jsgraph_; }
42 RepresentationChanger* changer() { return &changer_; }
44 // TODO(titzer): use ValueChecker / ValueUtil
45 void CheckInt32Constant(Node* n, int32_t expected) {
48 CHECK_EQ(expected, m.Value());
51 void CheckUint32Constant(Node* n, uint32_t expected) {
54 CHECK_EQ(static_cast<int>(expected), static_cast<int>(m.Value()));
57 void CheckFloat64Constant(Node* n, double expected) {
60 CHECK_EQ(expected, m.Value());
63 void CheckFloat32Constant(Node* n, float expected) {
64 CHECK_EQ(IrOpcode::kFloat32Constant, n->opcode());
65 float fval = OpParameter<float>(n->op());
66 CHECK_EQ(expected, fval);
69 void CheckHeapConstant(Node* n, HeapObject* expected) {
70 HeapObjectMatcher<HeapObject> m(n);
72 CHECK_EQ(expected, *m.Value().handle());
75 void CheckNumberConstant(Node* n, double expected) {
77 CHECK_EQ(IrOpcode::kNumberConstant, n->opcode());
79 CHECK_EQ(expected, m.Value());
82 Node* Parameter(int index = 0) {
83 return graph()->NewNode(common()->Parameter(index), graph()->start());
86 void CheckTypeError(MachineTypeUnion from, MachineTypeUnion to) {
87 changer()->testing_type_errors_ = true;
88 changer()->type_error_ = false;
89 Node* n = Parameter(0);
90 Node* c = changer()->GetRepresentationFor(n, from, to);
91 CHECK(changer()->type_error_);
95 void CheckNop(MachineTypeUnion from, MachineTypeUnion to) {
96 Node* n = Parameter(0);
97 Node* c = changer()->GetRepresentationFor(n, from, to);
103 } // namespace v8::internal::compiler
106 static const MachineType all_reps[] = {kRepBit, kRepWord32, kRepWord64,
107 kRepFloat32, kRepFloat64, kRepTagged};
110 TEST(BoolToBit_constant) {
111 RepresentationChangerTester r;
113 Node* true_node = r.jsgraph()->TrueConstant();
115 r.changer()->GetRepresentationFor(true_node, kRepTagged, kRepBit);
116 r.CheckInt32Constant(true_bit, 1);
118 Node* false_node = r.jsgraph()->FalseConstant();
120 r.changer()->GetRepresentationFor(false_node, kRepTagged, kRepBit);
121 r.CheckInt32Constant(false_bit, 0);
125 TEST(BitToBool_constant) {
126 RepresentationChangerTester r;
128 for (int i = -5; i < 5; i++) {
129 Node* node = r.jsgraph()->Int32Constant(i);
130 Node* val = r.changer()->GetRepresentationFor(node, kRepBit, kRepTagged);
131 r.CheckHeapConstant(val, i == 0 ? r.isolate()->heap()->false_value()
132 : r.isolate()->heap()->true_value());
137 TEST(ToTagged_constant) {
138 RepresentationChangerTester r;
141 FOR_FLOAT64_INPUTS(i) {
142 Node* n = r.jsgraph()->Float64Constant(*i);
143 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
144 r.CheckNumberConstant(c, *i);
149 FOR_FLOAT64_INPUTS(i) {
150 Node* n = r.jsgraph()->Constant(*i);
151 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
152 r.CheckNumberConstant(c, *i);
157 FOR_FLOAT32_INPUTS(i) {
158 Node* n = r.jsgraph()->Float32Constant(*i);
159 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32, kRepTagged);
160 r.CheckNumberConstant(c, *i);
165 FOR_INT32_INPUTS(i) {
166 Node* n = r.jsgraph()->Int32Constant(*i);
167 Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
169 r.CheckNumberConstant(c, *i);
174 FOR_UINT32_INPUTS(i) {
175 Node* n = r.jsgraph()->Int32Constant(*i);
176 Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
178 r.CheckNumberConstant(c, *i);
184 TEST(ToFloat64_constant) {
185 RepresentationChangerTester r;
188 FOR_FLOAT64_INPUTS(i) {
189 Node* n = r.jsgraph()->Float64Constant(*i);
190 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepFloat64);
196 FOR_FLOAT64_INPUTS(i) {
197 Node* n = r.jsgraph()->Constant(*i);
198 Node* c = r.changer()->GetRepresentationFor(n, kRepTagged, kRepFloat64);
199 r.CheckFloat64Constant(c, *i);
204 FOR_FLOAT32_INPUTS(i) {
205 Node* n = r.jsgraph()->Float32Constant(*i);
206 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32, kRepFloat64);
207 r.CheckFloat64Constant(c, *i);
212 FOR_INT32_INPUTS(i) {
213 Node* n = r.jsgraph()->Int32Constant(*i);
214 Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
216 r.CheckFloat64Constant(c, *i);
221 FOR_UINT32_INPUTS(i) {
222 Node* n = r.jsgraph()->Int32Constant(*i);
223 Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
225 r.CheckFloat64Constant(c, *i);
231 static bool IsFloat32Int32(int32_t val) {
232 return val >= -(1 << 23) && val <= (1 << 23);
236 static bool IsFloat32Uint32(uint32_t val) { return val <= (1 << 23); }
239 TEST(ToFloat32_constant) {
240 RepresentationChangerTester r;
243 FOR_FLOAT32_INPUTS(i) {
244 Node* n = r.jsgraph()->Float32Constant(*i);
245 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32, kRepFloat32);
251 FOR_FLOAT32_INPUTS(i) {
252 Node* n = r.jsgraph()->Constant(*i);
253 Node* c = r.changer()->GetRepresentationFor(n, kRepTagged, kRepFloat32);
254 r.CheckFloat32Constant(c, *i);
259 FOR_FLOAT32_INPUTS(i) {
260 Node* n = r.jsgraph()->Float64Constant(*i);
261 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepFloat32);
262 r.CheckFloat32Constant(c, *i);
267 FOR_INT32_INPUTS(i) {
268 if (!IsFloat32Int32(*i)) continue;
269 Node* n = r.jsgraph()->Int32Constant(*i);
270 Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
272 r.CheckFloat32Constant(c, static_cast<float>(*i));
277 FOR_UINT32_INPUTS(i) {
278 if (!IsFloat32Uint32(*i)) continue;
279 Node* n = r.jsgraph()->Int32Constant(*i);
280 Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
282 r.CheckFloat32Constant(c, static_cast<float>(*i));
288 TEST(ToInt32_constant) {
289 RepresentationChangerTester r;
292 FOR_INT32_INPUTS(i) {
293 Node* n = r.jsgraph()->Int32Constant(*i);
294 Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
296 r.CheckInt32Constant(c, *i);
301 FOR_INT32_INPUTS(i) {
302 if (!IsFloat32Int32(*i)) continue;
303 Node* n = r.jsgraph()->Float32Constant(static_cast<float>(*i));
304 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32 | kTypeInt32,
306 r.CheckInt32Constant(c, *i);
311 FOR_INT32_INPUTS(i) {
312 Node* n = r.jsgraph()->Float64Constant(*i);
313 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64 | kTypeInt32,
315 r.CheckInt32Constant(c, *i);
320 FOR_INT32_INPUTS(i) {
321 Node* n = r.jsgraph()->Constant(*i);
322 Node* c = r.changer()->GetRepresentationFor(n, kRepTagged | kTypeInt32,
324 r.CheckInt32Constant(c, *i);
330 TEST(ToUint32_constant) {
331 RepresentationChangerTester r;
334 FOR_UINT32_INPUTS(i) {
335 Node* n = r.jsgraph()->Int32Constant(*i);
336 Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
338 r.CheckUint32Constant(c, *i);
343 FOR_UINT32_INPUTS(i) {
344 if (!IsFloat32Uint32(*i)) continue;
345 Node* n = r.jsgraph()->Float32Constant(static_cast<float>(*i));
346 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32 | kTypeUint32,
348 r.CheckUint32Constant(c, *i);
353 FOR_UINT32_INPUTS(i) {
354 Node* n = r.jsgraph()->Float64Constant(*i);
355 Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64 | kTypeUint32,
357 r.CheckUint32Constant(c, *i);
362 FOR_UINT32_INPUTS(i) {
363 Node* n = r.jsgraph()->Constant(static_cast<double>(*i));
364 Node* c = r.changer()->GetRepresentationFor(n, kRepTagged | kTypeUint32,
366 r.CheckUint32Constant(c, *i);
372 static void CheckChange(IrOpcode::Value expected, MachineTypeUnion from,
373 MachineTypeUnion to) {
374 RepresentationChangerTester r;
376 Node* n = r.Parameter();
377 Node* c = r.changer()->GetRepresentationFor(n, from, to);
380 CHECK_EQ(expected, c->opcode());
381 CHECK_EQ(n, c->InputAt(0));
385 static void CheckTwoChanges(IrOpcode::Value expected2,
386 IrOpcode::Value expected1, MachineTypeUnion from,
387 MachineTypeUnion to) {
388 RepresentationChangerTester r;
390 Node* n = r.Parameter();
391 Node* c1 = r.changer()->GetRepresentationFor(n, from, to);
394 CHECK_EQ(expected1, c1->opcode());
395 Node* c2 = c1->InputAt(0);
397 CHECK_EQ(expected2, c2->opcode());
398 CHECK_EQ(n, c2->InputAt(0));
402 TEST(SingleChanges) {
403 CheckChange(IrOpcode::kChangeBoolToBit, kRepTagged, kRepBit);
404 CheckChange(IrOpcode::kChangeBitToBool, kRepBit, kRepTagged);
406 CheckChange(IrOpcode::kChangeInt32ToTagged, kRepWord32 | kTypeInt32,
408 CheckChange(IrOpcode::kChangeUint32ToTagged, kRepWord32 | kTypeUint32,
410 CheckChange(IrOpcode::kChangeFloat64ToTagged, kRepFloat64, kRepTagged);
412 CheckChange(IrOpcode::kChangeTaggedToInt32, kRepTagged | kTypeInt32,
414 CheckChange(IrOpcode::kChangeTaggedToUint32, kRepTagged | kTypeUint32,
416 CheckChange(IrOpcode::kChangeTaggedToFloat64, kRepTagged, kRepFloat64);
418 // Int32,Uint32 <-> Float64 are actually machine conversions.
419 CheckChange(IrOpcode::kChangeInt32ToFloat64, kRepWord32 | kTypeInt32,
421 CheckChange(IrOpcode::kChangeUint32ToFloat64, kRepWord32 | kTypeUint32,
423 CheckChange(IrOpcode::kChangeFloat64ToInt32, kRepFloat64 | kTypeInt32,
425 CheckChange(IrOpcode::kChangeFloat64ToUint32, kRepFloat64 | kTypeUint32,
428 CheckChange(IrOpcode::kTruncateFloat64ToFloat32, kRepFloat64, kRepFloat32);
430 // Int32,Uint32 <-> Float32 require two changes.
431 CheckTwoChanges(IrOpcode::kChangeInt32ToFloat64,
432 IrOpcode::kTruncateFloat64ToFloat32, kRepWord32 | kTypeInt32,
434 CheckTwoChanges(IrOpcode::kChangeUint32ToFloat64,
435 IrOpcode::kTruncateFloat64ToFloat32, kRepWord32 | kTypeUint32,
437 CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
438 IrOpcode::kChangeFloat64ToInt32, kRepFloat32 | kTypeInt32,
440 CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
441 IrOpcode::kChangeFloat64ToUint32, kRepFloat32 | kTypeUint32,
444 // Float32 <-> Tagged require two changes.
445 CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
446 IrOpcode::kChangeFloat64ToTagged, kRepFloat32, kRepTagged);
447 CheckTwoChanges(IrOpcode::kChangeTaggedToFloat64,
448 IrOpcode::kTruncateFloat64ToFloat32, kRepTagged, kRepFloat32);
452 TEST(SignednessInWord32) {
453 RepresentationChangerTester r;
455 // TODO(titzer): assume that uses of a word32 without a sign mean kTypeInt32.
456 CheckChange(IrOpcode::kChangeTaggedToInt32, kRepTagged,
457 kRepWord32 | kTypeInt32);
458 CheckChange(IrOpcode::kChangeTaggedToUint32, kRepTagged,
459 kRepWord32 | kTypeUint32);
460 CheckChange(IrOpcode::kChangeInt32ToFloat64, kRepWord32, kRepFloat64);
461 CheckChange(IrOpcode::kChangeFloat64ToInt32, kRepFloat64, kRepWord32);
463 CheckTwoChanges(IrOpcode::kChangeInt32ToFloat64,
464 IrOpcode::kTruncateFloat64ToFloat32, kRepWord32, kRepFloat32);
465 CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
466 IrOpcode::kChangeFloat64ToInt32, kRepFloat32, kRepWord32);
471 RepresentationChangerTester r;
473 // X -> X is always a nop for any single representation X.
474 for (size_t i = 0; i < arraysize(all_reps); i++) {
475 r.CheckNop(all_reps[i], all_reps[i]);
479 r.CheckNop(kRepFloat32, kRepFloat32);
480 r.CheckNop(kRepFloat32 | kTypeNumber, kRepFloat32);
481 r.CheckNop(kRepFloat32, kRepFloat32 | kTypeNumber);
483 // 32-bit or 64-bit words can be used as branch conditions (kRepBit).
484 r.CheckNop(kRepWord32, kRepBit);
485 r.CheckNop(kRepWord32, kRepBit | kTypeBool);
486 r.CheckNop(kRepWord64, kRepBit);
487 r.CheckNop(kRepWord64, kRepBit | kTypeBool);
489 // 32-bit words can be used as smaller word sizes and vice versa, because
490 // loads from memory implicitly sign or zero extend the value to the
491 // full machine word size, and stores implicitly truncate.
492 r.CheckNop(kRepWord32, kRepWord8);
493 r.CheckNop(kRepWord32, kRepWord16);
494 r.CheckNop(kRepWord32, kRepWord32);
495 r.CheckNop(kRepWord8, kRepWord32);
496 r.CheckNop(kRepWord16, kRepWord32);
498 // kRepBit (result of comparison) is implicitly a wordish thing.
499 r.CheckNop(kRepBit, kRepWord8);
500 r.CheckNop(kRepBit | kTypeBool, kRepWord8);
501 r.CheckNop(kRepBit, kRepWord16);
502 r.CheckNop(kRepBit | kTypeBool, kRepWord16);
503 r.CheckNop(kRepBit, kRepWord32);
504 r.CheckNop(kRepBit | kTypeBool, kRepWord32);
505 r.CheckNop(kRepBit, kRepWord64);
506 r.CheckNop(kRepBit | kTypeBool, kRepWord64);
511 RepresentationChangerTester r;
513 // Floats cannot be implicitly converted to/from comparison conditions.
514 r.CheckTypeError(kRepFloat64, kRepBit);
515 r.CheckTypeError(kRepFloat64, kRepBit | kTypeBool);
516 r.CheckTypeError(kRepBit, kRepFloat64);
517 r.CheckTypeError(kRepBit | kTypeBool, kRepFloat64);
519 // Floats cannot be implicitly converted to/from comparison conditions.
520 r.CheckTypeError(kRepFloat32, kRepBit);
521 r.CheckTypeError(kRepFloat32, kRepBit | kTypeBool);
522 r.CheckTypeError(kRepBit, kRepFloat32);
523 r.CheckTypeError(kRepBit | kTypeBool, kRepFloat32);
525 // Word64 is internal and shouldn't be implicitly converted.
526 r.CheckTypeError(kRepWord64, kRepTagged | kTypeBool);
527 r.CheckTypeError(kRepWord64, kRepTagged);
528 r.CheckTypeError(kRepWord64, kRepTagged | kTypeBool);
529 r.CheckTypeError(kRepTagged, kRepWord64);
530 r.CheckTypeError(kRepTagged | kTypeBool, kRepWord64);
532 // Word64 / Word32 shouldn't be implicitly converted.
533 r.CheckTypeError(kRepWord64, kRepWord32);
534 r.CheckTypeError(kRepWord32, kRepWord64);
535 r.CheckTypeError(kRepWord64, kRepWord32 | kTypeInt32);
536 r.CheckTypeError(kRepWord32 | kTypeInt32, kRepWord64);
537 r.CheckTypeError(kRepWord64, kRepWord32 | kTypeUint32);
538 r.CheckTypeError(kRepWord32 | kTypeUint32, kRepWord64);
540 for (size_t i = 0; i < arraysize(all_reps); i++) {
541 for (size_t j = 0; j < arraysize(all_reps); j++) {
542 if (i == j) continue;
543 // Only a single from representation is allowed.
544 r.CheckTypeError(all_reps[i] | all_reps[j], kRepTagged);