// Copyright 2014 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. #ifndef V8_COMPILER_REPRESENTATION_CHANGE_H_ #define V8_COMPILER_REPRESENTATION_CHANGE_H_ #include #include "src/base/bits.h" #include "src/compiler/js-graph.h" #include "src/compiler/machine-operator.h" #include "src/compiler/simplified-operator.h" namespace v8 { namespace internal { namespace compiler { // Contains logic related to changing the representation of values for constants // and other nodes, as well as lowering Simplified->Machine operators. // Eagerly folds any representation changes for constants. class RepresentationChanger { public: RepresentationChanger(JSGraph* jsgraph, SimplifiedOperatorBuilder* simplified, Isolate* isolate) : jsgraph_(jsgraph), simplified_(simplified), isolate_(isolate), testing_type_errors_(false), type_error_(false) {} // TODO(titzer): should Word64 also be implicitly convertable to others? static const MachineTypeUnion rWord = kRepWord8 | kRepWord16 | kRepWord32; Node* GetRepresentationFor(Node* node, MachineTypeUnion output_type, MachineTypeUnion use_type) { if (!base::bits::IsPowerOfTwo32(output_type & kRepMask)) { // There should be only one output representation. return TypeError(node, output_type, use_type); } if ((use_type & kRepMask) == (output_type & kRepMask)) { // Representations are the same. That's a no-op. return node; } if ((use_type & rWord) && (output_type & rWord)) { // 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; } if (use_type & kRepTagged) { return GetTaggedRepresentationFor(node, output_type); } else if (use_type & kRepFloat32) { return GetFloat32RepresentationFor(node, output_type); } else if (use_type & kRepFloat64) { return GetFloat64RepresentationFor(node, output_type); } else if (use_type & kRepBit) { return GetBitRepresentationFor(node, output_type); } else if (use_type & rWord) { return GetWord32RepresentationFor(node, output_type, use_type & kTypeUint32); } else if (use_type & kRepWord64) { return GetWord64RepresentationFor(node, output_type); } else { return node; } } Node* GetTaggedRepresentationFor(Node* node, MachineTypeUnion 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 & kTypeUint32) { uint32_t value = OpParameter(node); return jsgraph()->Constant(static_cast(value)); } else if (output_type & kTypeInt32) { int32_t value = OpParameter(node); return jsgraph()->Constant(value); } else if (output_type & kRepBit) { return OpParameter(node) == 0 ? jsgraph()->FalseConstant() : jsgraph()->TrueConstant(); } else { return TypeError(node, output_type, kRepTagged); } 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_type & kRepBit) { op = simplified()->ChangeBitToBool(); } else if (output_type & rWord) { if (output_type & kTypeUint32) { op = simplified()->ChangeUint32ToTagged(); } else if (output_type & kTypeInt32) { op = simplified()->ChangeInt32ToTagged(); } else { return TypeError(node, output_type, kRepTagged); } } else if (output_type & kRepFloat32) { // float32 -> float64 -> tagged node = InsertChangeFloat32ToFloat64(node); op = simplified()->ChangeFloat64ToTagged(); } else if (output_type & kRepFloat64) { op = simplified()->ChangeFloat64ToTagged(); } else { return TypeError(node, output_type, kRepTagged); } return jsgraph()->graph()->NewNode(op, node); } Node* GetFloat32RepresentationFor(Node* node, MachineTypeUnion output_type) { // 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 & kTypeUint32) { uint32_t value = 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_type & kRepBit) { return TypeError(node, output_type, kRepFloat32); } else if (output_type & rWord) { if (output_type & kTypeUint32) { op = machine()->ChangeUint32ToFloat64(); } else { op = machine()->ChangeInt32ToFloat64(); } // int32 -> float64 -> float32 node = jsgraph()->graph()->NewNode(op, node); op = machine()->TruncateFloat64ToFloat32(); } else if (output_type & kRepTagged) { op = simplified() ->ChangeTaggedToFloat64(); // tagged -> float64 -> float32 node = jsgraph()->graph()->NewNode(op, node); op = machine()->TruncateFloat64ToFloat32(); } else if (output_type & kRepFloat64) { op = machine()->TruncateFloat64ToFloat32(); } else { return TypeError(node, output_type, kRepFloat32); } return jsgraph()->graph()->NewNode(op, node); } Node* GetFloat64RepresentationFor(Node* node, MachineTypeUnion output_type) { // Eagerly fold representation changes for constants. switch (node->opcode()) { case IrOpcode::kNumberConstant: return jsgraph()->Float64Constant(OpParameter(node)); case IrOpcode::kInt32Constant: if (output_type & kTypeUint32) { uint32_t value = OpParameter(node); return jsgraph()->Float64Constant(static_cast(value)); } else { int32_t value = OpParameter(node); return jsgraph()->Float64Constant(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_type & kRepBit) { return TypeError(node, output_type, kRepFloat64); } else if (output_type & rWord) { if (output_type & kTypeUint32) { op = machine()->ChangeUint32ToFloat64(); } else { op = machine()->ChangeInt32ToFloat64(); } } else if (output_type & kRepTagged) { op = simplified()->ChangeTaggedToFloat64(); } else if (output_type & kRepFloat32) { op = machine()->ChangeFloat32ToFloat64(); } else { return TypeError(node, output_type, kRepFloat64); } return jsgraph()->graph()->NewNode(op, node); } Node* MakeInt32Constant(double value) { if (value < 0) { DCHECK(IsInt32Double(value)); int32_t iv = static_cast(value); return jsgraph()->Int32Constant(iv); } else { DCHECK(IsUint32Double(value)); int32_t iv = static_cast(static_cast(value)); return jsgraph()->Int32Constant(iv); } } Node* GetTruncatedWord32For(Node* node, MachineTypeUnion output_type) { // Eagerly fold truncations for constants. switch (node->opcode()) { case IrOpcode::kInt32Constant: return node; // No change necessary. case IrOpcode::kFloat32Constant: return jsgraph()->Int32Constant( DoubleToInt32(OpParameter(node))); case IrOpcode::kNumberConstant: case IrOpcode::kFloat64Constant: return jsgraph()->Int32Constant( DoubleToInt32(OpParameter(node))); default: break; } // Select the correct X -> Word32 truncation operator. const Operator* op = NULL; if (output_type & kRepFloat64) { op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); } else if (output_type & kRepFloat32) { node = InsertChangeFloat32ToFloat64(node); op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); } else if (output_type & kRepTagged) { node = InsertChangeTaggedToFloat64(node); op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); } else { return TypeError(node, output_type, kRepWord32); } return jsgraph()->graph()->NewNode(op, node); } Node* GetWord32RepresentationFor(Node* node, MachineTypeUnion output_type, bool use_unsigned) { // Eagerly fold representation changes for constants. switch (node->opcode()) { case IrOpcode::kInt32Constant: return node; // No change necessary. case IrOpcode::kFloat32Constant: return MakeInt32Constant(OpParameter(node)); case IrOpcode::kNumberConstant: case IrOpcode::kFloat64Constant: return MakeInt32Constant(OpParameter(node)); default: break; } // Select the correct X -> Word32 operator. const Operator* op; if (output_type & kRepBit) { return node; // Sloppy comparison -> word32 } else if (output_type & kRepFloat64) { if (output_type & kTypeUint32 || use_unsigned) { op = machine()->ChangeFloat64ToUint32(); } else { op = machine()->ChangeFloat64ToInt32(); } } else if (output_type & kRepFloat32) { node = InsertChangeFloat32ToFloat64(node); // float32 -> float64 -> int32 if (output_type & kTypeUint32 || use_unsigned) { op = machine()->ChangeFloat64ToUint32(); } else { op = machine()->ChangeFloat64ToInt32(); } } else if (output_type & kRepTagged) { if (output_type & kTypeUint32 || use_unsigned) { op = simplified()->ChangeTaggedToUint32(); } else { op = simplified()->ChangeTaggedToInt32(); } } else { return TypeError(node, output_type, kRepWord32); } return jsgraph()->graph()->NewNode(op, node); } Node* GetBitRepresentationFor(Node* node, MachineTypeUnion output_type) { // Eagerly fold representation changes for constants. switch (node->opcode()) { case IrOpcode::kHeapConstant: { Handle value = OpParameter >(node).handle(); 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_type & kRepTagged) { op = simplified()->ChangeBoolToBit(); } else { return TypeError(node, output_type, kRepBit); } return jsgraph()->graph()->NewNode(op, node); } Node* GetWord64RepresentationFor(Node* node, MachineTypeUnion output_type) { if (output_type & kRepBit) { return node; // Sloppy comparison -> word64 } // Can't really convert Word64 to anything else. Purported to be internal. return TypeError(node, output_type, kRepWord64); } const Operator* 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::kNumberEqual: return machine()->Word32Equal(); case IrOpcode::kNumberLessThan: return machine()->Int32LessThan(); case IrOpcode::kNumberLessThanOrEqual: return machine()->Int32LessThanOrEqual(); default: UNREACHABLE(); return NULL; } } const Operator* 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 NULL; } } const Operator* 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 NULL; } } MachineType TypeForBasePointer(const FieldAccess& access) { return access.tag() != 0 ? kMachAnyTagged : kMachPtr; } MachineType TypeForBasePointer(const ElementAccess& access) { return access.tag() != 0 ? kMachAnyTagged : kMachPtr; } MachineType TypeFromUpperBound(Type* type) { if (type->Is(Type::None())) return kTypeAny; // TODO(titzer): should be an error if (type->Is(Type::Signed32())) return kTypeInt32; if (type->Is(Type::Unsigned32())) return kTypeUint32; if (type->Is(Type::Number())) return kTypeNumber; if (type->Is(Type::Boolean())) return kTypeBool; return kTypeAny; } private: JSGraph* jsgraph_; SimplifiedOperatorBuilder* simplified_; Isolate* isolate_; friend class RepresentationChangerTester; // accesses the below fields. bool testing_type_errors_; // If {true}, don't abort on a type error. bool type_error_; // Set when a type error is detected. Node* TypeError(Node* node, MachineTypeUnion output_type, MachineTypeUnion use) { type_error_ = true; if (!testing_type_errors_) { std::ostringstream out_str; out_str << static_cast(output_type); std::ostringstream use_str; use_str << static_cast(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* InsertChangeFloat32ToFloat64(Node* node) { return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node); } Node* InsertChangeTaggedToFloat64(Node* node) { return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(), node); } JSGraph* jsgraph() const { return jsgraph_; } Isolate* isolate() const { return isolate_; } Factory* factory() const { return isolate()->factory(); } SimplifiedOperatorBuilder* simplified() { return simplified_; } MachineOperatorBuilder* machine() { return jsgraph()->machine(); } }; } // namespace compiler } // namespace internal } // namespace v8 #endif // V8_COMPILER_REPRESENTATION_CHANGE_H_