Home | History | Annotate | Download | only in compiler
      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 <limits>
      6 
      7 #include "src/v8.h"
      8 #include "test/cctest/cctest.h"
      9 #include "test/cctest/compiler/graph-builder-tester.h"
     10 
     11 #include "src/compiler/node-matchers.h"
     12 #include "src/compiler/representation-change.h"
     13 #include "src/compiler/typer.h"
     14 
     15 using namespace v8::internal;
     16 using namespace v8::internal::compiler;
     17 
     18 namespace v8 {  // for friendiness.
     19 namespace internal {
     20 namespace compiler {
     21 
     22 class RepresentationChangerTester : public HandleAndZoneScope,
     23                                     public GraphAndBuilders {
     24  public:
     25   explicit RepresentationChangerTester(int num_parameters = 0)
     26       : GraphAndBuilders(main_zone()),
     27         typer_(main_zone()),
     28         javascript_(main_zone()),
     29         jsgraph_(main_graph_, &main_common_, &javascript_, &typer_,
     30                  &main_machine_),
     31         changer_(&jsgraph_, &main_simplified_, main_isolate()) {
     32     Node* s = graph()->NewNode(common()->Start(num_parameters));
     33     graph()->SetStart(s);
     34   }
     35 
     36   Typer typer_;
     37   JSOperatorBuilder javascript_;
     38   JSGraph jsgraph_;
     39   RepresentationChanger changer_;
     40 
     41   Isolate* isolate() { return main_isolate(); }
     42   Graph* graph() { return main_graph_; }
     43   CommonOperatorBuilder* common() { return &main_common_; }
     44   JSGraph* jsgraph() { return &jsgraph_; }
     45   RepresentationChanger* changer() { return &changer_; }
     46 
     47   // TODO(titzer): use ValueChecker / ValueUtil
     48   void CheckInt32Constant(Node* n, int32_t expected) {
     49     Int32Matcher m(n);
     50     CHECK(m.HasValue());
     51     CHECK_EQ(expected, m.Value());
     52   }
     53 
     54   void CheckHeapConstant(Node* n, HeapObject* expected) {
     55     HeapObjectMatcher<HeapObject> m(n);
     56     CHECK(m.HasValue());
     57     CHECK_EQ(expected, *m.Value().handle());
     58   }
     59 
     60   void CheckNumberConstant(Node* n, double expected) {
     61     NumberMatcher m(n);
     62     CHECK_EQ(IrOpcode::kNumberConstant, n->opcode());
     63     CHECK(m.HasValue());
     64     CHECK_EQ(expected, m.Value());
     65   }
     66 
     67   Node* Parameter(int index = 0) {
     68     return graph()->NewNode(common()->Parameter(index), graph()->start());
     69   }
     70 
     71   void CheckTypeError(MachineTypeUnion from, MachineTypeUnion to) {
     72     changer()->testing_type_errors_ = true;
     73     changer()->type_error_ = false;
     74     Node* n = Parameter(0);
     75     Node* c = changer()->GetRepresentationFor(n, from, to);
     76     CHECK(changer()->type_error_);
     77     CHECK_EQ(n, c);
     78   }
     79 
     80   void CheckNop(MachineTypeUnion from, MachineTypeUnion to) {
     81     Node* n = Parameter(0);
     82     Node* c = changer()->GetRepresentationFor(n, from, to);
     83     CHECK_EQ(n, c);
     84   }
     85 };
     86 }
     87 }
     88 }  // namespace v8::internal::compiler
     89 
     90 
     91 // TODO(titzer): add kRepFloat32 when fully supported.
     92 static const MachineType all_reps[] = {kRepBit, kRepWord32, kRepWord64,
     93                                        kRepFloat64, kRepTagged};
     94 
     95 
     96 // TODO(titzer): lift this to ValueHelper
     97 static const double double_inputs[] = {
     98     0.0,   -0.0,    1.0,    -1.0,        0.1,         1.4,    -1.7,
     99     2,     5,       6,      982983,      888,         -999.8, 3.1e7,
    100     -2e66, 2.3e124, -12e73, V8_INFINITY, -V8_INFINITY};
    101 
    102 
    103 static const int32_t int32_inputs[] = {
    104     0,      1,                                -1,
    105     2,      5,                                6,
    106     982983, 888,                              -999,
    107     65535,  static_cast<int32_t>(0xFFFFFFFF), static_cast<int32_t>(0x80000000)};
    108 
    109 
    110 static const uint32_t uint32_inputs[] = {
    111     0,      1,   static_cast<uint32_t>(-1),   2,     5,          6,
    112     982983, 888, static_cast<uint32_t>(-999), 65535, 0xFFFFFFFF, 0x80000000};
    113 
    114 
    115 TEST(BoolToBit_constant) {
    116   RepresentationChangerTester r;
    117 
    118   Node* true_node = r.jsgraph()->TrueConstant();
    119   Node* true_bit =
    120       r.changer()->GetRepresentationFor(true_node, kRepTagged, kRepBit);
    121   r.CheckInt32Constant(true_bit, 1);
    122 
    123   Node* false_node = r.jsgraph()->FalseConstant();
    124   Node* false_bit =
    125       r.changer()->GetRepresentationFor(false_node, kRepTagged, kRepBit);
    126   r.CheckInt32Constant(false_bit, 0);
    127 }
    128 
    129 
    130 TEST(BitToBool_constant) {
    131   RepresentationChangerTester r;
    132 
    133   for (int i = -5; i < 5; i++) {
    134     Node* node = r.jsgraph()->Int32Constant(i);
    135     Node* val = r.changer()->GetRepresentationFor(node, kRepBit, kRepTagged);
    136     r.CheckHeapConstant(val, i == 0 ? r.isolate()->heap()->false_value()
    137                                     : r.isolate()->heap()->true_value());
    138   }
    139 }
    140 
    141 
    142 TEST(ToTagged_constant) {
    143   RepresentationChangerTester r;
    144 
    145   for (size_t i = 0; i < arraysize(double_inputs); i++) {
    146     Node* n = r.jsgraph()->Float64Constant(double_inputs[i]);
    147     Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
    148     r.CheckNumberConstant(c, double_inputs[i]);
    149   }
    150 
    151   for (size_t i = 0; i < arraysize(int32_inputs); i++) {
    152     Node* n = r.jsgraph()->Int32Constant(int32_inputs[i]);
    153     Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
    154                                                 kRepTagged);
    155     r.CheckNumberConstant(c, static_cast<double>(int32_inputs[i]));
    156   }
    157 
    158   for (size_t i = 0; i < arraysize(uint32_inputs); i++) {
    159     Node* n = r.jsgraph()->Int32Constant(uint32_inputs[i]);
    160     Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
    161                                                 kRepTagged);
    162     r.CheckNumberConstant(c, static_cast<double>(uint32_inputs[i]));
    163   }
    164 }
    165 
    166 
    167 static void CheckChange(IrOpcode::Value expected, MachineTypeUnion from,
    168                         MachineTypeUnion to) {
    169   RepresentationChangerTester r;
    170 
    171   Node* n = r.Parameter();
    172   Node* c = r.changer()->GetRepresentationFor(n, from, to);
    173 
    174   CHECK_NE(c, n);
    175   CHECK_EQ(expected, c->opcode());
    176   CHECK_EQ(n, c->InputAt(0));
    177 }
    178 
    179 
    180 TEST(SingleChanges) {
    181   CheckChange(IrOpcode::kChangeBoolToBit, kRepTagged, kRepBit);
    182   CheckChange(IrOpcode::kChangeBitToBool, kRepBit, kRepTagged);
    183 
    184   CheckChange(IrOpcode::kChangeInt32ToTagged, kRepWord32 | kTypeInt32,
    185               kRepTagged);
    186   CheckChange(IrOpcode::kChangeUint32ToTagged, kRepWord32 | kTypeUint32,
    187               kRepTagged);
    188   CheckChange(IrOpcode::kChangeFloat64ToTagged, kRepFloat64, kRepTagged);
    189 
    190   CheckChange(IrOpcode::kChangeTaggedToInt32, kRepTagged | kTypeInt32,
    191               kRepWord32);
    192   CheckChange(IrOpcode::kChangeTaggedToUint32, kRepTagged | kTypeUint32,
    193               kRepWord32);
    194   CheckChange(IrOpcode::kChangeTaggedToFloat64, kRepTagged, kRepFloat64);
    195 
    196   // Int32,Uint32 <-> Float64 are actually machine conversions.
    197   CheckChange(IrOpcode::kChangeInt32ToFloat64, kRepWord32 | kTypeInt32,
    198               kRepFloat64);
    199   CheckChange(IrOpcode::kChangeUint32ToFloat64, kRepWord32 | kTypeUint32,
    200               kRepFloat64);
    201   CheckChange(IrOpcode::kChangeFloat64ToInt32, kRepFloat64 | kTypeInt32,
    202               kRepWord32);
    203   CheckChange(IrOpcode::kChangeFloat64ToUint32, kRepFloat64 | kTypeUint32,
    204               kRepWord32);
    205 }
    206 
    207 
    208 TEST(SignednessInWord32) {
    209   RepresentationChangerTester r;
    210 
    211   // TODO(titzer): assume that uses of a word32 without a sign mean kTypeInt32.
    212   CheckChange(IrOpcode::kChangeTaggedToInt32, kRepTagged,
    213               kRepWord32 | kTypeInt32);
    214   CheckChange(IrOpcode::kChangeTaggedToUint32, kRepTagged,
    215               kRepWord32 | kTypeUint32);
    216   CheckChange(IrOpcode::kChangeInt32ToFloat64, kRepWord32, kRepFloat64);
    217   CheckChange(IrOpcode::kChangeFloat64ToInt32, kRepFloat64, kRepWord32);
    218 }
    219 
    220 
    221 TEST(Nops) {
    222   RepresentationChangerTester r;
    223 
    224   // X -> X is always a nop for any single representation X.
    225   for (size_t i = 0; i < arraysize(all_reps); i++) {
    226     r.CheckNop(all_reps[i], all_reps[i]);
    227   }
    228 
    229   // 32-bit floats.
    230   r.CheckNop(kRepFloat32, kRepFloat32);
    231   r.CheckNop(kRepFloat32 | kTypeNumber, kRepFloat32);
    232   r.CheckNop(kRepFloat32, kRepFloat32 | kTypeNumber);
    233 
    234   // 32-bit or 64-bit words can be used as branch conditions (kRepBit).
    235   r.CheckNop(kRepWord32, kRepBit);
    236   r.CheckNop(kRepWord32, kRepBit | kTypeBool);
    237   r.CheckNop(kRepWord64, kRepBit);
    238   r.CheckNop(kRepWord64, kRepBit | kTypeBool);
    239 
    240   // 32-bit words can be used as smaller word sizes and vice versa, because
    241   // loads from memory implicitly sign or zero extend the value to the
    242   // full machine word size, and stores implicitly truncate.
    243   r.CheckNop(kRepWord32, kRepWord8);
    244   r.CheckNop(kRepWord32, kRepWord16);
    245   r.CheckNop(kRepWord32, kRepWord32);
    246   r.CheckNop(kRepWord8, kRepWord32);
    247   r.CheckNop(kRepWord16, kRepWord32);
    248 
    249   // kRepBit (result of comparison) is implicitly a wordish thing.
    250   r.CheckNop(kRepBit, kRepWord8);
    251   r.CheckNop(kRepBit | kTypeBool, kRepWord8);
    252   r.CheckNop(kRepBit, kRepWord16);
    253   r.CheckNop(kRepBit | kTypeBool, kRepWord16);
    254   r.CheckNop(kRepBit, kRepWord32);
    255   r.CheckNop(kRepBit | kTypeBool, kRepWord32);
    256   r.CheckNop(kRepBit, kRepWord64);
    257   r.CheckNop(kRepBit | kTypeBool, kRepWord64);
    258 }
    259 
    260 
    261 TEST(TypeErrors) {
    262   RepresentationChangerTester r;
    263 
    264   // Floats cannot be implicitly converted to/from comparison conditions.
    265   r.CheckTypeError(kRepFloat64, kRepBit);
    266   r.CheckTypeError(kRepFloat64, kRepBit | kTypeBool);
    267   r.CheckTypeError(kRepBit, kRepFloat64);
    268   r.CheckTypeError(kRepBit | kTypeBool, kRepFloat64);
    269 
    270   // Floats cannot be implicitly converted to/from comparison conditions.
    271   r.CheckTypeError(kRepFloat32, kRepBit);
    272   r.CheckTypeError(kRepFloat32, kRepBit | kTypeBool);
    273   r.CheckTypeError(kRepBit, kRepFloat32);
    274   r.CheckTypeError(kRepBit | kTypeBool, kRepFloat32);
    275 
    276   // Word64 is internal and shouldn't be implicitly converted.
    277   r.CheckTypeError(kRepWord64, kRepTagged | kTypeBool);
    278   r.CheckTypeError(kRepWord64, kRepTagged);
    279   r.CheckTypeError(kRepWord64, kRepTagged | kTypeBool);
    280   r.CheckTypeError(kRepTagged, kRepWord64);
    281   r.CheckTypeError(kRepTagged | kTypeBool, kRepWord64);
    282 
    283   // Word64 / Word32 shouldn't be implicitly converted.
    284   r.CheckTypeError(kRepWord64, kRepWord32);
    285   r.CheckTypeError(kRepWord32, kRepWord64);
    286   r.CheckTypeError(kRepWord64, kRepWord32 | kTypeInt32);
    287   r.CheckTypeError(kRepWord32 | kTypeInt32, kRepWord64);
    288   r.CheckTypeError(kRepWord64, kRepWord32 | kTypeUint32);
    289   r.CheckTypeError(kRepWord32 | kTypeUint32, kRepWord64);
    290 
    291   for (size_t i = 0; i < arraysize(all_reps); i++) {
    292     for (size_t j = 0; j < arraysize(all_reps); j++) {
    293       if (i == j) continue;
    294       // Only a single from representation is allowed.
    295       r.CheckTypeError(all_reps[i] | all_reps[j], kRepTagged);
    296     }
    297   }
    298 
    299   // TODO(titzer): Float32 representation changes trigger type errors now.
    300   // Enforce current behavior to test all paths through representation changer.
    301   for (size_t i = 0; i < arraysize(all_reps); i++) {
    302     r.CheckTypeError(all_reps[i], kRepFloat32);
    303     r.CheckTypeError(kRepFloat32, all_reps[i]);
    304   }
    305 }
    306