Home | History | Annotate | Download | only in compiler
      1 // Copyright 2015 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/compiler/representation-change.h"
      6 
      7 #include <sstream>
      8 
      9 #include "src/base/bits.h"
     10 #include "src/code-factory.h"
     11 #include "src/compiler/machine-operator.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 namespace compiler {
     16 
     17 const char* Truncation::description() const {
     18   switch (kind()) {
     19     case TruncationKind::kNone:
     20       return "no-value-use";
     21     case TruncationKind::kBool:
     22       return "truncate-to-bool";
     23     case TruncationKind::kWord32:
     24       return "truncate-to-word32";
     25     case TruncationKind::kWord64:
     26       return "truncate-to-word64";
     27     case TruncationKind::kFloat32:
     28       return "truncate-to-float32";
     29     case TruncationKind::kFloat64:
     30       return "truncate-to-float64";
     31     case TruncationKind::kAny:
     32       return "no-truncation";
     33   }
     34   UNREACHABLE();
     35   return nullptr;
     36 }
     37 
     38 
     39 // Partial order for truncations:
     40 //
     41 //  kWord64       kAny
     42 //     ^            ^
     43 //     \            |
     44 //      \         kFloat64  <--+
     45 //       \        ^    ^       |
     46 //        \       /    |       |
     47 //         kWord32  kFloat32  kBool
     48 //               ^     ^      ^
     49 //               \     |      /
     50 //                \    |     /
     51 //                 \   |    /
     52 //                  \  |   /
     53 //                   \ |  /
     54 //                   kNone
     55 
     56 // static
     57 Truncation::TruncationKind Truncation::Generalize(TruncationKind rep1,
     58                                                   TruncationKind rep2) {
     59   if (LessGeneral(rep1, rep2)) return rep2;
     60   if (LessGeneral(rep2, rep1)) return rep1;
     61   // Handle the generalization of float64-representable values.
     62   if (LessGeneral(rep1, TruncationKind::kFloat64) &&
     63       LessGeneral(rep2, TruncationKind::kFloat64)) {
     64     return TruncationKind::kFloat64;
     65   }
     66   // All other combinations are illegal.
     67   FATAL("Tried to combine incompatible truncations");
     68   return TruncationKind::kNone;
     69 }
     70 
     71 
     72 // static
     73 bool Truncation::LessGeneral(TruncationKind rep1, TruncationKind rep2) {
     74   switch (rep1) {
     75     case TruncationKind::kNone:
     76       return true;
     77     case TruncationKind::kBool:
     78       return rep2 == TruncationKind::kBool || rep2 == TruncationKind::kAny;
     79     case TruncationKind::kWord32:
     80       return rep2 == TruncationKind::kWord32 ||
     81              rep2 == TruncationKind::kWord64 ||
     82              rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny;
     83     case TruncationKind::kWord64:
     84       return rep2 == TruncationKind::kWord64;
     85     case TruncationKind::kFloat32:
     86       return rep2 == TruncationKind::kFloat32 ||
     87              rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny;
     88     case TruncationKind::kFloat64:
     89       return rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny;
     90     case TruncationKind::kAny:
     91       return rep2 == TruncationKind::kAny;
     92   }
     93   UNREACHABLE();
     94   return false;
     95 }
     96 
     97 
     98 namespace {
     99 
    100 // TODO(titzer): should Word64 also be implicitly convertable to others?
    101 bool IsWord(MachineRepresentation rep) {
    102   return rep == MachineRepresentation::kWord8 ||
    103          rep == MachineRepresentation::kWord16 ||
    104          rep == MachineRepresentation::kWord32;
    105 }
    106 
    107 }  // namespace
    108 
    109 
    110 // Changes representation from {output_rep} to {use_rep}. The {truncation}
    111 // parameter is only used for sanity checking - if the changer cannot figure
    112 // out signedness for the word32->float64 conversion, then we check that the
    113 // uses truncate to word32 (so they do not care about signedness).
    114 Node* RepresentationChanger::GetRepresentationFor(
    115     Node* node, MachineRepresentation output_rep, Type* output_type,
    116     MachineRepresentation use_rep, Truncation truncation) {
    117   if (output_rep == MachineRepresentation::kNone) {
    118     // The output representation should be set.
    119     return TypeError(node, output_rep, output_type, use_rep);
    120   }
    121   if (use_rep == output_rep) {
    122     // Representations are the same. That's a no-op.
    123     return node;
    124   }
    125   if (IsWord(use_rep) && IsWord(output_rep)) {
    126     // Both are words less than or equal to 32-bits.
    127     // Since loads of integers from memory implicitly sign or zero extend the
    128     // value to the full machine word size and stores implicitly truncate,
    129     // no representation change is necessary.
    130     return node;
    131   }
    132   switch (use_rep) {
    133     case MachineRepresentation::kTagged:
    134       return GetTaggedRepresentationFor(node, output_rep, output_type);
    135     case MachineRepresentation::kFloat32:
    136       return GetFloat32RepresentationFor(node, output_rep, output_type,
    137                                          truncation);
    138     case MachineRepresentation::kFloat64:
    139       return GetFloat64RepresentationFor(node, output_rep, output_type,
    140                                          truncation);
    141     case MachineRepresentation::kBit:
    142       return GetBitRepresentationFor(node, output_rep, output_type);
    143     case MachineRepresentation::kWord8:
    144     case MachineRepresentation::kWord16:
    145     case MachineRepresentation::kWord32:
    146       return GetWord32RepresentationFor(node, output_rep, output_type);
    147     case MachineRepresentation::kWord64:
    148       return GetWord64RepresentationFor(node, output_rep, output_type);
    149     case MachineRepresentation::kNone:
    150       return node;
    151   }
    152   UNREACHABLE();
    153   return nullptr;
    154 }
    155 
    156 
    157 Node* RepresentationChanger::GetTaggedRepresentationFor(
    158     Node* node, MachineRepresentation output_rep, Type* output_type) {
    159   // Eagerly fold representation changes for constants.
    160   switch (node->opcode()) {
    161     case IrOpcode::kNumberConstant:
    162     case IrOpcode::kHeapConstant:
    163       return node;  // No change necessary.
    164     case IrOpcode::kInt32Constant:
    165       if (output_type->Is(Type::Signed32())) {
    166         int32_t value = OpParameter<int32_t>(node);
    167         return jsgraph()->Constant(value);
    168       } else if (output_type->Is(Type::Unsigned32())) {
    169         uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
    170         return jsgraph()->Constant(static_cast<double>(value));
    171       } else if (output_rep == MachineRepresentation::kBit) {
    172         return OpParameter<int32_t>(node) == 0 ? jsgraph()->FalseConstant()
    173                                                : jsgraph()->TrueConstant();
    174       } else {
    175         return TypeError(node, output_rep, output_type,
    176                          MachineRepresentation::kTagged);
    177       }
    178     case IrOpcode::kFloat64Constant:
    179       return jsgraph()->Constant(OpParameter<double>(node));
    180     case IrOpcode::kFloat32Constant:
    181       return jsgraph()->Constant(OpParameter<float>(node));
    182     default:
    183       break;
    184   }
    185   // Select the correct X -> Tagged operator.
    186   const Operator* op;
    187   if (output_rep == MachineRepresentation::kBit) {
    188     op = simplified()->ChangeBitToBool();
    189   } else if (IsWord(output_rep)) {
    190     if (output_type->Is(Type::Unsigned32())) {
    191       op = simplified()->ChangeUint32ToTagged();
    192     } else if (output_type->Is(Type::Signed32())) {
    193       op = simplified()->ChangeInt32ToTagged();
    194     } else {
    195       return TypeError(node, output_rep, output_type,
    196                        MachineRepresentation::kTagged);
    197     }
    198   } else if (output_rep ==
    199              MachineRepresentation::kFloat32) {  // float32 -> float64 -> tagged
    200     node = InsertChangeFloat32ToFloat64(node);
    201     op = simplified()->ChangeFloat64ToTagged();
    202   } else if (output_rep == MachineRepresentation::kFloat64) {
    203     op = simplified()->ChangeFloat64ToTagged();
    204   } else {
    205     return TypeError(node, output_rep, output_type,
    206                      MachineRepresentation::kTagged);
    207   }
    208   return jsgraph()->graph()->NewNode(op, node);
    209 }
    210 
    211 
    212 Node* RepresentationChanger::GetFloat32RepresentationFor(
    213     Node* node, MachineRepresentation output_rep, Type* output_type,
    214     Truncation truncation) {
    215   // Eagerly fold representation changes for constants.
    216   switch (node->opcode()) {
    217     case IrOpcode::kFloat64Constant:
    218     case IrOpcode::kNumberConstant:
    219       return jsgraph()->Float32Constant(
    220           DoubleToFloat32(OpParameter<double>(node)));
    221     case IrOpcode::kInt32Constant:
    222       if (output_type->Is(Type::Unsigned32())) {
    223         uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
    224         return jsgraph()->Float32Constant(static_cast<float>(value));
    225       } else {
    226         int32_t value = OpParameter<int32_t>(node);
    227         return jsgraph()->Float32Constant(static_cast<float>(value));
    228       }
    229     case IrOpcode::kFloat32Constant:
    230       return node;  // No change necessary.
    231     default:
    232       break;
    233   }
    234   // Select the correct X -> Float32 operator.
    235   const Operator* op;
    236   if (output_rep == MachineRepresentation::kBit) {
    237     return TypeError(node, output_rep, output_type,
    238                      MachineRepresentation::kFloat32);
    239   } else if (IsWord(output_rep)) {
    240     if (output_type->Is(Type::Signed32())) {
    241       op = machine()->ChangeInt32ToFloat64();
    242     } else {
    243       // Either the output is int32 or the uses only care about the
    244       // low 32 bits (so we can pick int32 safely).
    245       DCHECK(output_type->Is(Type::Unsigned32()) ||
    246              truncation.TruncatesToWord32());
    247       op = machine()->ChangeUint32ToFloat64();
    248     }
    249     // int32 -> float64 -> float32
    250     node = jsgraph()->graph()->NewNode(op, node);
    251     op = machine()->TruncateFloat64ToFloat32();
    252   } else if (output_rep == MachineRepresentation::kTagged) {
    253     op = simplified()->ChangeTaggedToFloat64();  // tagged -> float64 -> float32
    254     node = jsgraph()->graph()->NewNode(op, node);
    255     op = machine()->TruncateFloat64ToFloat32();
    256   } else if (output_rep == MachineRepresentation::kFloat64) {
    257     op = machine()->TruncateFloat64ToFloat32();
    258   } else {
    259     return TypeError(node, output_rep, output_type,
    260                      MachineRepresentation::kFloat32);
    261   }
    262   return jsgraph()->graph()->NewNode(op, node);
    263 }
    264 
    265 
    266 Node* RepresentationChanger::GetFloat64RepresentationFor(
    267     Node* node, MachineRepresentation output_rep, Type* output_type,
    268     Truncation truncation) {
    269   // Eagerly fold representation changes for constants.
    270   switch (node->opcode()) {
    271     case IrOpcode::kNumberConstant:
    272       return jsgraph()->Float64Constant(OpParameter<double>(node));
    273     case IrOpcode::kInt32Constant:
    274       if (output_type->Is(Type::Signed32())) {
    275         int32_t value = OpParameter<int32_t>(node);
    276         return jsgraph()->Float64Constant(value);
    277       } else {
    278         DCHECK(output_type->Is(Type::Unsigned32()));
    279         uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
    280         return jsgraph()->Float64Constant(static_cast<double>(value));
    281       }
    282     case IrOpcode::kFloat64Constant:
    283       return node;  // No change necessary.
    284     case IrOpcode::kFloat32Constant:
    285       return jsgraph()->Float64Constant(OpParameter<float>(node));
    286     default:
    287       break;
    288   }
    289   // Select the correct X -> Float64 operator.
    290   const Operator* op;
    291   if (output_rep == MachineRepresentation::kBit) {
    292     return TypeError(node, output_rep, output_type,
    293                      MachineRepresentation::kFloat64);
    294   } else if (IsWord(output_rep)) {
    295     if (output_type->Is(Type::Signed32())) {
    296       op = machine()->ChangeInt32ToFloat64();
    297     } else {
    298       // Either the output is int32 or the uses only care about the
    299       // low 32 bits (so we can pick int32 safely).
    300       DCHECK(output_type->Is(Type::Unsigned32()) ||
    301              truncation.TruncatesToWord32());
    302       op = machine()->ChangeUint32ToFloat64();
    303     }
    304   } else if (output_rep == MachineRepresentation::kTagged) {
    305     op = simplified()->ChangeTaggedToFloat64();
    306   } else if (output_rep == MachineRepresentation::kFloat32) {
    307     op = machine()->ChangeFloat32ToFloat64();
    308   } else {
    309     return TypeError(node, output_rep, output_type,
    310                      MachineRepresentation::kFloat64);
    311   }
    312   return jsgraph()->graph()->NewNode(op, node);
    313 }
    314 
    315 
    316 Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) {
    317   return jsgraph()->Int32Constant(DoubleToInt32(value));
    318 }
    319 
    320 
    321 Node* RepresentationChanger::GetWord32RepresentationFor(
    322     Node* node, MachineRepresentation output_rep, Type* output_type) {
    323   // Eagerly fold representation changes for constants.
    324   switch (node->opcode()) {
    325     case IrOpcode::kInt32Constant:
    326       return node;  // No change necessary.
    327     case IrOpcode::kFloat32Constant:
    328       return MakeTruncatedInt32Constant(OpParameter<float>(node));
    329     case IrOpcode::kNumberConstant:
    330     case IrOpcode::kFloat64Constant:
    331       return MakeTruncatedInt32Constant(OpParameter<double>(node));
    332     default:
    333       break;
    334   }
    335   // Select the correct X -> Word32 operator.
    336   const Operator* op;
    337   Type* type = NodeProperties::GetType(node);
    338 
    339   if (output_rep == MachineRepresentation::kBit) {
    340     return node;  // Sloppy comparison -> word32
    341   } else if (output_rep == MachineRepresentation::kFloat64) {
    342     // TODO(jarin) Use only output_type here, once we intersect it with the
    343     // type inferred by the typer.
    344     if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) {
    345       op = machine()->ChangeFloat64ToUint32();
    346     } else if (output_type->Is(Type::Signed32()) ||
    347                type->Is(Type::Signed32())) {
    348       op = machine()->ChangeFloat64ToInt32();
    349     } else {
    350       op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
    351     }
    352   } else if (output_rep == MachineRepresentation::kFloat32) {
    353     node = InsertChangeFloat32ToFloat64(node);  // float32 -> float64 -> int32
    354     if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) {
    355       op = machine()->ChangeFloat64ToUint32();
    356     } else if (output_type->Is(Type::Signed32()) ||
    357                type->Is(Type::Signed32())) {
    358       op = machine()->ChangeFloat64ToInt32();
    359     } else {
    360       op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
    361     }
    362   } else if (output_rep == MachineRepresentation::kTagged) {
    363     if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) {
    364       op = simplified()->ChangeTaggedToUint32();
    365     } else if (output_type->Is(Type::Signed32()) ||
    366                type->Is(Type::Signed32())) {
    367       op = simplified()->ChangeTaggedToInt32();
    368     } else {
    369       node = InsertChangeTaggedToFloat64(node);
    370       op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
    371     }
    372   } else {
    373     return TypeError(node, output_rep, output_type,
    374                      MachineRepresentation::kWord32);
    375   }
    376   return jsgraph()->graph()->NewNode(op, node);
    377 }
    378 
    379 
    380 Node* RepresentationChanger::GetBitRepresentationFor(
    381     Node* node, MachineRepresentation output_rep, Type* output_type) {
    382   // Eagerly fold representation changes for constants.
    383   switch (node->opcode()) {
    384     case IrOpcode::kHeapConstant: {
    385       Handle<HeapObject> value = OpParameter<Handle<HeapObject>>(node);
    386       DCHECK(value.is_identical_to(factory()->true_value()) ||
    387              value.is_identical_to(factory()->false_value()));
    388       return jsgraph()->Int32Constant(
    389           value.is_identical_to(factory()->true_value()) ? 1 : 0);
    390     }
    391     default:
    392       break;
    393   }
    394   // Select the correct X -> Bit operator.
    395   const Operator* op;
    396   if (output_rep == MachineRepresentation::kTagged) {
    397     op = simplified()->ChangeBoolToBit();
    398   } else {
    399     return TypeError(node, output_rep, output_type,
    400                      MachineRepresentation::kBit);
    401   }
    402   return jsgraph()->graph()->NewNode(op, node);
    403 }
    404 
    405 
    406 Node* RepresentationChanger::GetWord64RepresentationFor(
    407     Node* node, MachineRepresentation output_rep, Type* output_type) {
    408   if (output_rep == MachineRepresentation::kBit) {
    409     return node;  // Sloppy comparison -> word64
    410   }
    411   // Can't really convert Word64 to anything else. Purported to be internal.
    412   return TypeError(node, output_rep, output_type,
    413                    MachineRepresentation::kWord64);
    414 }
    415 
    416 
    417 const Operator* RepresentationChanger::Int32OperatorFor(
    418     IrOpcode::Value opcode) {
    419   switch (opcode) {
    420     case IrOpcode::kNumberAdd:
    421       return machine()->Int32Add();
    422     case IrOpcode::kNumberSubtract:
    423       return machine()->Int32Sub();
    424     case IrOpcode::kNumberMultiply:
    425       return machine()->Int32Mul();
    426     case IrOpcode::kNumberDivide:
    427       return machine()->Int32Div();
    428     case IrOpcode::kNumberModulus:
    429       return machine()->Int32Mod();
    430     case IrOpcode::kNumberBitwiseOr:
    431       return machine()->Word32Or();
    432     case IrOpcode::kNumberBitwiseXor:
    433       return machine()->Word32Xor();
    434     case IrOpcode::kNumberBitwiseAnd:
    435       return machine()->Word32And();
    436     case IrOpcode::kNumberEqual:
    437       return machine()->Word32Equal();
    438     case IrOpcode::kNumberLessThan:
    439       return machine()->Int32LessThan();
    440     case IrOpcode::kNumberLessThanOrEqual:
    441       return machine()->Int32LessThanOrEqual();
    442     default:
    443       UNREACHABLE();
    444       return nullptr;
    445   }
    446 }
    447 
    448 
    449 const Operator* RepresentationChanger::Uint32OperatorFor(
    450     IrOpcode::Value opcode) {
    451   switch (opcode) {
    452     case IrOpcode::kNumberAdd:
    453       return machine()->Int32Add();
    454     case IrOpcode::kNumberSubtract:
    455       return machine()->Int32Sub();
    456     case IrOpcode::kNumberMultiply:
    457       return machine()->Int32Mul();
    458     case IrOpcode::kNumberDivide:
    459       return machine()->Uint32Div();
    460     case IrOpcode::kNumberModulus:
    461       return machine()->Uint32Mod();
    462     case IrOpcode::kNumberEqual:
    463       return machine()->Word32Equal();
    464     case IrOpcode::kNumberLessThan:
    465       return machine()->Uint32LessThan();
    466     case IrOpcode::kNumberLessThanOrEqual:
    467       return machine()->Uint32LessThanOrEqual();
    468     default:
    469       UNREACHABLE();
    470       return nullptr;
    471   }
    472 }
    473 
    474 
    475 const Operator* RepresentationChanger::Float64OperatorFor(
    476     IrOpcode::Value opcode) {
    477   switch (opcode) {
    478     case IrOpcode::kNumberAdd:
    479       return machine()->Float64Add();
    480     case IrOpcode::kNumberSubtract:
    481       return machine()->Float64Sub();
    482     case IrOpcode::kNumberMultiply:
    483       return machine()->Float64Mul();
    484     case IrOpcode::kNumberDivide:
    485       return machine()->Float64Div();
    486     case IrOpcode::kNumberModulus:
    487       return machine()->Float64Mod();
    488     case IrOpcode::kNumberEqual:
    489       return machine()->Float64Equal();
    490     case IrOpcode::kNumberLessThan:
    491       return machine()->Float64LessThan();
    492     case IrOpcode::kNumberLessThanOrEqual:
    493       return machine()->Float64LessThanOrEqual();
    494     default:
    495       UNREACHABLE();
    496       return nullptr;
    497   }
    498 }
    499 
    500 
    501 Node* RepresentationChanger::TypeError(Node* node,
    502                                        MachineRepresentation output_rep,
    503                                        Type* output_type,
    504                                        MachineRepresentation use) {
    505   type_error_ = true;
    506   if (!testing_type_errors_) {
    507     std::ostringstream out_str;
    508     out_str << output_rep << " (";
    509     output_type->PrintTo(out_str, Type::SEMANTIC_DIM);
    510     out_str << ")";
    511 
    512     std::ostringstream use_str;
    513     use_str << use;
    514 
    515     V8_Fatal(__FILE__, __LINE__,
    516              "RepresentationChangerError: node #%d:%s of "
    517              "%s cannot be changed to %s",
    518              node->id(), node->op()->mnemonic(), out_str.str().c_str(),
    519              use_str.str().c_str());
    520   }
    521   return node;
    522 }
    523 
    524 
    525 Node* RepresentationChanger::InsertChangeFloat32ToFloat64(Node* node) {
    526   return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node);
    527 }
    528 
    529 
    530 Node* RepresentationChanger::InsertChangeTaggedToFloat64(Node* node) {
    531   return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(),
    532                                      node);
    533 }
    534 
    535 }  // namespace compiler
    536 }  // namespace internal
    537 }  // namespace v8
    538