// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler/representation-change.h" #include #include "src/base/bits.h" #include "src/code-factory.h" #include "src/compiler/machine-operator.h" namespace v8 { namespace internal { namespace compiler { const char* Truncation::description() const { switch (kind()) { case TruncationKind::kNone: return "no-value-use"; case TruncationKind::kBool: return "truncate-to-bool"; case TruncationKind::kWord32: return "truncate-to-word32"; case TruncationKind::kWord64: return "truncate-to-word64"; case TruncationKind::kFloat32: return "truncate-to-float32"; case TruncationKind::kFloat64: return "truncate-to-float64"; case TruncationKind::kAny: return "no-truncation"; } UNREACHABLE(); return nullptr; } // Partial order for truncations: // // kWord64 kAny // ^ ^ // \ | // \ kFloat64 <--+ // \ ^ ^ | // \ / | | // kWord32 kFloat32 kBool // ^ ^ ^ // \ | / // \ | / // \ | / // \ | / // \ | / // kNone // static Truncation::TruncationKind Truncation::Generalize(TruncationKind rep1, TruncationKind rep2) { if (LessGeneral(rep1, rep2)) return rep2; if (LessGeneral(rep2, rep1)) return rep1; // Handle the generalization of float64-representable values. if (LessGeneral(rep1, TruncationKind::kFloat64) && LessGeneral(rep2, TruncationKind::kFloat64)) { return TruncationKind::kFloat64; } // All other combinations are illegal. FATAL("Tried to combine incompatible truncations"); return TruncationKind::kNone; } // static bool Truncation::LessGeneral(TruncationKind rep1, TruncationKind rep2) { switch (rep1) { case TruncationKind::kNone: return true; case TruncationKind::kBool: return rep2 == TruncationKind::kBool || rep2 == TruncationKind::kAny; case TruncationKind::kWord32: return rep2 == TruncationKind::kWord32 || rep2 == TruncationKind::kWord64 || rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny; case TruncationKind::kWord64: return rep2 == TruncationKind::kWord64; case TruncationKind::kFloat32: return rep2 == TruncationKind::kFloat32 || rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny; case TruncationKind::kFloat64: return rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny; case TruncationKind::kAny: return rep2 == TruncationKind::kAny; } UNREACHABLE(); return false; } namespace { // TODO(titzer): should Word64 also be implicitly convertable to others? bool IsWord(MachineRepresentation rep) { return rep == MachineRepresentation::kWord8 || rep == MachineRepresentation::kWord16 || rep == MachineRepresentation::kWord32; } } // namespace // Changes representation from {output_rep} to {use_rep}. The {truncation} // parameter is only used for sanity checking - if the changer cannot figure // out signedness for the word32->float64 conversion, then we check that the // uses truncate to word32 (so they do not care about signedness). Node* RepresentationChanger::GetRepresentationFor( Node* node, MachineRepresentation output_rep, Type* output_type, MachineRepresentation use_rep, Truncation truncation) { if (output_rep == MachineRepresentation::kNone) { // The output representation should be set. return TypeError(node, output_rep, output_type, use_rep); } if (use_rep == output_rep) { // Representations are the same. That's a no-op. return node; } if (IsWord(use_rep) && IsWord(output_rep)) { // Both are words less than or equal to 32-bits. // Since loads of integers from memory implicitly sign or zero extend the // value to the full machine word size and stores implicitly truncate, // no representation change is necessary. return node; } switch (use_rep) { case MachineRepresentation::kTagged: return GetTaggedRepresentationFor(node, output_rep, output_type); case MachineRepresentation::kFloat32: return GetFloat32RepresentationFor(node, output_rep, output_type, truncation); case MachineRepresentation::kFloat64: return GetFloat64RepresentationFor(node, output_rep, output_type, truncation); case MachineRepresentation::kBit: return GetBitRepresentationFor(node, output_rep, output_type); case MachineRepresentation::kWord8: case MachineRepresentation::kWord16: case MachineRepresentation::kWord32: return GetWord32RepresentationFor(node, output_rep, output_type); case MachineRepresentation::kWord64: return GetWord64RepresentationFor(node, output_rep, output_type); case MachineRepresentation::kNone: return node; } UNREACHABLE(); return nullptr; } Node* RepresentationChanger::GetTaggedRepresentationFor( Node* node, MachineRepresentation output_rep, Type* output_type) { // Eagerly fold representation changes for constants. switch (node->opcode()) { case IrOpcode::kNumberConstant: case IrOpcode::kHeapConstant: return node; // No change necessary. case IrOpcode::kInt32Constant: if (output_type->Is(Type::Signed32())) { int32_t value = OpParameter(node); return jsgraph()->Constant(value); } else if (output_type->Is(Type::Unsigned32())) { uint32_t value = static_cast(OpParameter(node)); return jsgraph()->Constant(static_cast(value)); } else if (output_rep == MachineRepresentation::kBit) { return OpParameter(node) == 0 ? jsgraph()->FalseConstant() : jsgraph()->TrueConstant(); } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kTagged); } case IrOpcode::kFloat64Constant: return jsgraph()->Constant(OpParameter(node)); case IrOpcode::kFloat32Constant: return jsgraph()->Constant(OpParameter(node)); default: break; } // Select the correct X -> Tagged operator. const Operator* op; if (output_rep == MachineRepresentation::kBit) { op = simplified()->ChangeBitToBool(); } else if (IsWord(output_rep)) { if (output_type->Is(Type::Unsigned32())) { op = simplified()->ChangeUint32ToTagged(); } else if (output_type->Is(Type::Signed32())) { op = simplified()->ChangeInt32ToTagged(); } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kTagged); } } else if (output_rep == MachineRepresentation::kFloat32) { // float32 -> float64 -> tagged node = InsertChangeFloat32ToFloat64(node); op = simplified()->ChangeFloat64ToTagged(); } else if (output_rep == MachineRepresentation::kFloat64) { op = simplified()->ChangeFloat64ToTagged(); } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kTagged); } return jsgraph()->graph()->NewNode(op, node); } Node* RepresentationChanger::GetFloat32RepresentationFor( Node* node, MachineRepresentation output_rep, Type* output_type, Truncation truncation) { // Eagerly fold representation changes for constants. switch (node->opcode()) { case IrOpcode::kFloat64Constant: case IrOpcode::kNumberConstant: return jsgraph()->Float32Constant( DoubleToFloat32(OpParameter(node))); case IrOpcode::kInt32Constant: if (output_type->Is(Type::Unsigned32())) { uint32_t value = static_cast(OpParameter(node)); return jsgraph()->Float32Constant(static_cast(value)); } else { int32_t value = OpParameter(node); return jsgraph()->Float32Constant(static_cast(value)); } case IrOpcode::kFloat32Constant: return node; // No change necessary. default: break; } // Select the correct X -> Float32 operator. const Operator* op; if (output_rep == MachineRepresentation::kBit) { return TypeError(node, output_rep, output_type, MachineRepresentation::kFloat32); } else if (IsWord(output_rep)) { if (output_type->Is(Type::Signed32())) { op = machine()->ChangeInt32ToFloat64(); } else { // Either the output is int32 or the uses only care about the // low 32 bits (so we can pick int32 safely). DCHECK(output_type->Is(Type::Unsigned32()) || truncation.TruncatesToWord32()); op = machine()->ChangeUint32ToFloat64(); } // int32 -> float64 -> float32 node = jsgraph()->graph()->NewNode(op, node); op = machine()->TruncateFloat64ToFloat32(); } else if (output_rep == MachineRepresentation::kTagged) { op = simplified()->ChangeTaggedToFloat64(); // tagged -> float64 -> float32 node = jsgraph()->graph()->NewNode(op, node); op = machine()->TruncateFloat64ToFloat32(); } else if (output_rep == MachineRepresentation::kFloat64) { op = machine()->TruncateFloat64ToFloat32(); } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kFloat32); } return jsgraph()->graph()->NewNode(op, node); } Node* RepresentationChanger::GetFloat64RepresentationFor( Node* node, MachineRepresentation output_rep, Type* output_type, Truncation truncation) { // Eagerly fold representation changes for constants. switch (node->opcode()) { case IrOpcode::kNumberConstant: return jsgraph()->Float64Constant(OpParameter(node)); case IrOpcode::kInt32Constant: if (output_type->Is(Type::Signed32())) { int32_t value = OpParameter(node); return jsgraph()->Float64Constant(value); } else { DCHECK(output_type->Is(Type::Unsigned32())); uint32_t value = static_cast(OpParameter(node)); return jsgraph()->Float64Constant(static_cast(value)); } case IrOpcode::kFloat64Constant: return node; // No change necessary. case IrOpcode::kFloat32Constant: return jsgraph()->Float64Constant(OpParameter(node)); default: break; } // Select the correct X -> Float64 operator. const Operator* op; if (output_rep == MachineRepresentation::kBit) { return TypeError(node, output_rep, output_type, MachineRepresentation::kFloat64); } else if (IsWord(output_rep)) { if (output_type->Is(Type::Signed32())) { op = machine()->ChangeInt32ToFloat64(); } else { // Either the output is int32 or the uses only care about the // low 32 bits (so we can pick int32 safely). DCHECK(output_type->Is(Type::Unsigned32()) || truncation.TruncatesToWord32()); op = machine()->ChangeUint32ToFloat64(); } } else if (output_rep == MachineRepresentation::kTagged) { op = simplified()->ChangeTaggedToFloat64(); } else if (output_rep == MachineRepresentation::kFloat32) { op = machine()->ChangeFloat32ToFloat64(); } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kFloat64); } return jsgraph()->graph()->NewNode(op, node); } Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) { return jsgraph()->Int32Constant(DoubleToInt32(value)); } Node* RepresentationChanger::GetWord32RepresentationFor( Node* node, MachineRepresentation output_rep, Type* output_type) { // Eagerly fold representation changes for constants. switch (node->opcode()) { case IrOpcode::kInt32Constant: return node; // No change necessary. case IrOpcode::kFloat32Constant: return MakeTruncatedInt32Constant(OpParameter(node)); case IrOpcode::kNumberConstant: case IrOpcode::kFloat64Constant: return MakeTruncatedInt32Constant(OpParameter(node)); default: break; } // Select the correct X -> Word32 operator. const Operator* op; Type* type = NodeProperties::GetType(node); if (output_rep == MachineRepresentation::kBit) { return node; // Sloppy comparison -> word32 } else if (output_rep == MachineRepresentation::kFloat64) { // TODO(jarin) Use only output_type here, once we intersect it with the // type inferred by the typer. if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) { op = machine()->ChangeFloat64ToUint32(); } else if (output_type->Is(Type::Signed32()) || type->Is(Type::Signed32())) { op = machine()->ChangeFloat64ToInt32(); } else { op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); } } else if (output_rep == MachineRepresentation::kFloat32) { node = InsertChangeFloat32ToFloat64(node); // float32 -> float64 -> int32 if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) { op = machine()->ChangeFloat64ToUint32(); } else if (output_type->Is(Type::Signed32()) || type->Is(Type::Signed32())) { op = machine()->ChangeFloat64ToInt32(); } else { op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); } } else if (output_rep == MachineRepresentation::kTagged) { if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) { op = simplified()->ChangeTaggedToUint32(); } else if (output_type->Is(Type::Signed32()) || type->Is(Type::Signed32())) { op = simplified()->ChangeTaggedToInt32(); } else { node = InsertChangeTaggedToFloat64(node); op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); } } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kWord32); } return jsgraph()->graph()->NewNode(op, node); } Node* RepresentationChanger::GetBitRepresentationFor( Node* node, MachineRepresentation output_rep, Type* output_type) { // Eagerly fold representation changes for constants. switch (node->opcode()) { case IrOpcode::kHeapConstant: { Handle value = OpParameter>(node); DCHECK(value.is_identical_to(factory()->true_value()) || value.is_identical_to(factory()->false_value())); return jsgraph()->Int32Constant( value.is_identical_to(factory()->true_value()) ? 1 : 0); } default: break; } // Select the correct X -> Bit operator. const Operator* op; if (output_rep == MachineRepresentation::kTagged) { op = simplified()->ChangeBoolToBit(); } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kBit); } return jsgraph()->graph()->NewNode(op, node); } Node* RepresentationChanger::GetWord64RepresentationFor( Node* node, MachineRepresentation output_rep, Type* output_type) { if (output_rep == MachineRepresentation::kBit) { return node; // Sloppy comparison -> word64 } // Can't really convert Word64 to anything else. Purported to be internal. return TypeError(node, output_rep, output_type, MachineRepresentation::kWord64); } const Operator* RepresentationChanger::Int32OperatorFor( IrOpcode::Value opcode) { switch (opcode) { case IrOpcode::kNumberAdd: return machine()->Int32Add(); case IrOpcode::kNumberSubtract: return machine()->Int32Sub(); case IrOpcode::kNumberMultiply: return machine()->Int32Mul(); case IrOpcode::kNumberDivide: return machine()->Int32Div(); case IrOpcode::kNumberModulus: return machine()->Int32Mod(); case IrOpcode::kNumberBitwiseOr: return machine()->Word32Or(); case IrOpcode::kNumberBitwiseXor: return machine()->Word32Xor(); case IrOpcode::kNumberBitwiseAnd: return machine()->Word32And(); case IrOpcode::kNumberEqual: return machine()->Word32Equal(); case IrOpcode::kNumberLessThan: return machine()->Int32LessThan(); case IrOpcode::kNumberLessThanOrEqual: return machine()->Int32LessThanOrEqual(); default: UNREACHABLE(); return nullptr; } } const Operator* RepresentationChanger::Uint32OperatorFor( IrOpcode::Value opcode) { switch (opcode) { case IrOpcode::kNumberAdd: return machine()->Int32Add(); case IrOpcode::kNumberSubtract: return machine()->Int32Sub(); case IrOpcode::kNumberMultiply: return machine()->Int32Mul(); case IrOpcode::kNumberDivide: return machine()->Uint32Div(); case IrOpcode::kNumberModulus: return machine()->Uint32Mod(); case IrOpcode::kNumberEqual: return machine()->Word32Equal(); case IrOpcode::kNumberLessThan: return machine()->Uint32LessThan(); case IrOpcode::kNumberLessThanOrEqual: return machine()->Uint32LessThanOrEqual(); default: UNREACHABLE(); return nullptr; } } const Operator* RepresentationChanger::Float64OperatorFor( IrOpcode::Value opcode) { switch (opcode) { case IrOpcode::kNumberAdd: return machine()->Float64Add(); case IrOpcode::kNumberSubtract: return machine()->Float64Sub(); case IrOpcode::kNumberMultiply: return machine()->Float64Mul(); case IrOpcode::kNumberDivide: return machine()->Float64Div(); case IrOpcode::kNumberModulus: return machine()->Float64Mod(); case IrOpcode::kNumberEqual: return machine()->Float64Equal(); case IrOpcode::kNumberLessThan: return machine()->Float64LessThan(); case IrOpcode::kNumberLessThanOrEqual: return machine()->Float64LessThanOrEqual(); default: UNREACHABLE(); return nullptr; } } Node* RepresentationChanger::TypeError(Node* node, MachineRepresentation output_rep, Type* output_type, MachineRepresentation use) { type_error_ = true; if (!testing_type_errors_) { std::ostringstream out_str; out_str << output_rep << " ("; output_type->PrintTo(out_str, Type::SEMANTIC_DIM); out_str << ")"; std::ostringstream use_str; use_str << use; V8_Fatal(__FILE__, __LINE__, "RepresentationChangerError: node #%d:%s of " "%s cannot be changed to %s", node->id(), node->op()->mnemonic(), out_str.str().c_str(), use_str.str().c_str()); } return node; } Node* RepresentationChanger::InsertChangeFloat32ToFloat64(Node* node) { return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node); } Node* RepresentationChanger::InsertChangeTaggedToFloat64(Node* node) { return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(), node); } } // namespace compiler } // namespace internal } // namespace v8