// 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 "src/compiler/js-graph.h" #include "src/compiler/simplified-operator.h" namespace v8 { namespace internal { namespace compiler { enum IdentifyZeros { kIdentifyZeros, kDistinguishZeros }; class Truncation final { public: // Constructors. static Truncation None() { return Truncation(TruncationKind::kNone, kIdentifyZeros); } static Truncation Bool() { return Truncation(TruncationKind::kBool, kIdentifyZeros); } static Truncation Word32() { return Truncation(TruncationKind::kWord32, kIdentifyZeros); } static Truncation Word64() { return Truncation(TruncationKind::kWord64, kIdentifyZeros); } static Truncation Float64(IdentifyZeros identify_zeros = kDistinguishZeros) { return Truncation(TruncationKind::kFloat64, identify_zeros); } static Truncation Any(IdentifyZeros identify_zeros = kDistinguishZeros) { return Truncation(TruncationKind::kAny, identify_zeros); } static Truncation Generalize(Truncation t1, Truncation t2) { return Truncation( Generalize(t1.kind(), t2.kind()), GeneralizeIdentifyZeros(t1.identify_zeros(), t2.identify_zeros())); } // Queries. bool IsUnused() const { return kind_ == TruncationKind::kNone; } bool IsUsedAsBool() const { return LessGeneral(kind_, TruncationKind::kBool); } bool IsUsedAsWord32() const { return LessGeneral(kind_, TruncationKind::kWord32); } bool IsUsedAsFloat64() const { return LessGeneral(kind_, TruncationKind::kFloat64); } bool IdentifiesUndefinedAndZero() { return LessGeneral(kind_, TruncationKind::kWord32) || LessGeneral(kind_, TruncationKind::kBool); } bool IdentifiesUndefinedAndNaN() { return LessGeneral(kind_, TruncationKind::kFloat64) || LessGeneral(kind_, TruncationKind::kWord64); } bool IdentifiesZeroAndMinusZero() const { return identify_zeros() == kIdentifyZeros; } // Operators. bool operator==(Truncation other) const { return kind() == other.kind() && identify_zeros() == other.identify_zeros(); } bool operator!=(Truncation other) const { return !(*this == other); } // Debug utilities. const char* description() const; bool IsLessGeneralThan(Truncation other) { return LessGeneral(kind(), other.kind()) && LessGeneralIdentifyZeros(identify_zeros(), other.identify_zeros()); } IdentifyZeros identify_zeros() const { return identify_zeros_; } private: enum class TruncationKind : uint8_t { kNone, kBool, kWord32, kWord64, kFloat64, kAny }; explicit Truncation(TruncationKind kind, IdentifyZeros identify_zeros) : kind_(kind), identify_zeros_(identify_zeros) { DCHECK(kind == TruncationKind::kAny || kind == TruncationKind::kFloat64 || identify_zeros == kIdentifyZeros); } TruncationKind kind() const { return kind_; } TruncationKind kind_; IdentifyZeros identify_zeros_; static TruncationKind Generalize(TruncationKind rep1, TruncationKind rep2); static IdentifyZeros GeneralizeIdentifyZeros(IdentifyZeros i1, IdentifyZeros i2); static bool LessGeneral(TruncationKind rep1, TruncationKind rep2); static bool LessGeneralIdentifyZeros(IdentifyZeros u1, IdentifyZeros u2); }; enum class TypeCheckKind : uint8_t { kNone, kSignedSmall, kSigned32, kNumber, kNumberOrOddball, kHeapObject }; inline std::ostream& operator<<(std::ostream& os, TypeCheckKind type_check) { switch (type_check) { case TypeCheckKind::kNone: return os << "None"; case TypeCheckKind::kSignedSmall: return os << "SignedSmall"; case TypeCheckKind::kSigned32: return os << "Signed32"; case TypeCheckKind::kNumber: return os << "Number"; case TypeCheckKind::kNumberOrOddball: return os << "NumberOrOddball"; case TypeCheckKind::kHeapObject: return os << "HeapObject"; } UNREACHABLE(); } // The {UseInfo} class is used to describe a use of an input of a node. // // This information is used in two different ways, based on the phase: // // 1. During propagation, the use info is used to inform the input node // about what part of the input is used (we call this truncation) and what // is the preferred representation. For conversions that will require // checks, we also keep track of whether a minus zero check is needed. // // 2. During lowering, the use info is used to properly convert the input // to the preferred representation. The preferred representation might be // insufficient to do the conversion (e.g. word32->float64 conv), so we also // need the signedness information to produce the correct value. // Additionally, use info may contain {CheckParameters} which contains // information for the deoptimizer such as a CallIC on which speculation // should be disallowed if the check fails. class UseInfo { public: UseInfo(MachineRepresentation representation, Truncation truncation, TypeCheckKind type_check = TypeCheckKind::kNone, const VectorSlotPair& feedback = VectorSlotPair()) : representation_(representation), truncation_(truncation), type_check_(type_check), feedback_(feedback) {} static UseInfo TruncatingWord32() { return UseInfo(MachineRepresentation::kWord32, Truncation::Word32()); } static UseInfo TruncatingWord64() { return UseInfo(MachineRepresentation::kWord64, Truncation::Word64()); } static UseInfo Bool() { return UseInfo(MachineRepresentation::kBit, Truncation::Bool()); } static UseInfo Float32() { return UseInfo(MachineRepresentation::kFloat32, Truncation::Any()); } static UseInfo TruncatingFloat64() { return UseInfo(MachineRepresentation::kFloat64, Truncation::Float64()); } static UseInfo PointerInt() { return kPointerSize == 4 ? TruncatingWord32() : TruncatingWord64(); } static UseInfo AnyTagged() { return UseInfo(MachineRepresentation::kTagged, Truncation::Any()); } static UseInfo TaggedSigned() { return UseInfo(MachineRepresentation::kTaggedSigned, Truncation::Any()); } static UseInfo TaggedPointer() { return UseInfo(MachineRepresentation::kTaggedPointer, Truncation::Any()); } // Possibly deoptimizing conversions. static UseInfo CheckedHeapObjectAsTaggedPointer() { return UseInfo(MachineRepresentation::kTaggedPointer, Truncation::Any(), TypeCheckKind::kHeapObject); } static UseInfo CheckedSignedSmallAsTaggedSigned( const VectorSlotPair& feedback) { return UseInfo(MachineRepresentation::kTaggedSigned, Truncation::Any(), TypeCheckKind::kSignedSmall, feedback); } static UseInfo CheckedSignedSmallAsWord32(IdentifyZeros identify_zeros, const VectorSlotPair& feedback) { return UseInfo(MachineRepresentation::kWord32, Truncation::Any(identify_zeros), TypeCheckKind::kSignedSmall, feedback); } static UseInfo CheckedSigned32AsWord32(IdentifyZeros identify_zeros, const VectorSlotPair& feedback) { return UseInfo(MachineRepresentation::kWord32, Truncation::Any(identify_zeros), TypeCheckKind::kSigned32, feedback); } static UseInfo CheckedNumberAsFloat64(const VectorSlotPair& feedback) { return UseInfo(MachineRepresentation::kFloat64, Truncation::Any(), TypeCheckKind::kNumber, feedback); } static UseInfo CheckedNumberAsWord32(const VectorSlotPair& feedback) { return UseInfo(MachineRepresentation::kWord32, Truncation::Word32(), TypeCheckKind::kNumber, feedback); } static UseInfo CheckedNumberOrOddballAsFloat64( const VectorSlotPair& feedback) { return UseInfo(MachineRepresentation::kFloat64, Truncation::Any(), TypeCheckKind::kNumberOrOddball, feedback); } static UseInfo CheckedNumberOrOddballAsWord32( const VectorSlotPair& feedback) { return UseInfo(MachineRepresentation::kWord32, Truncation::Word32(), TypeCheckKind::kNumberOrOddball, feedback); } // Undetermined representation. static UseInfo Any() { return UseInfo(MachineRepresentation::kNone, Truncation::Any()); } static UseInfo AnyTruncatingToBool() { return UseInfo(MachineRepresentation::kNone, Truncation::Bool()); } // Value not used. static UseInfo None() { return UseInfo(MachineRepresentation::kNone, Truncation::None()); } MachineRepresentation representation() const { return representation_; } Truncation truncation() const { return truncation_; } TypeCheckKind type_check() const { return type_check_; } CheckForMinusZeroMode minus_zero_check() const { return truncation().IdentifiesZeroAndMinusZero() ? CheckForMinusZeroMode::kDontCheckForMinusZero : CheckForMinusZeroMode::kCheckForMinusZero; } const VectorSlotPair& feedback() const { return feedback_; } private: MachineRepresentation representation_; Truncation truncation_; TypeCheckKind type_check_; VectorSlotPair feedback_; }; // 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 final { public: RepresentationChanger(JSGraph* jsgraph, Isolate* isolate) : jsgraph_(jsgraph), isolate_(isolate), testing_type_errors_(false), type_error_(false) {} // Changes representation from {output_type} 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* GetRepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type, Node* use_node, UseInfo use_info); const Operator* Int32OperatorFor(IrOpcode::Value opcode); const Operator* Int32OverflowOperatorFor(IrOpcode::Value opcode); const Operator* TaggedSignedOperatorFor(IrOpcode::Value opcode); const Operator* Uint32OperatorFor(IrOpcode::Value opcode); const Operator* Uint32OverflowOperatorFor(IrOpcode::Value opcode); const Operator* Float64OperatorFor(IrOpcode::Value opcode); MachineType TypeForBasePointer(const FieldAccess& access) { return access.tag() != 0 ? MachineType::AnyTagged() : MachineType::Pointer(); } MachineType TypeForBasePointer(const ElementAccess& access) { return access.tag() != 0 ? MachineType::AnyTagged() : MachineType::Pointer(); } private: JSGraph* jsgraph_; 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* GetTaggedSignedRepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type, Node* use_node, UseInfo use_info); Node* GetTaggedPointerRepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type, Node* use_node, UseInfo use_info); Node* GetTaggedRepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type, Truncation truncation); Node* GetFloat32RepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type, Truncation truncation); Node* GetFloat64RepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type, Node* use_node, UseInfo use_info); Node* GetWord32RepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type, Node* use_node, UseInfo use_info); Node* GetBitRepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type); Node* GetWord64RepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type); Node* TypeError(Node* node, MachineRepresentation output_rep, Type output_type, MachineRepresentation use); Node* MakeTruncatedInt32Constant(double value); Node* InsertChangeBitToTagged(Node* node); Node* InsertChangeFloat32ToFloat64(Node* node); Node* InsertChangeFloat64ToInt32(Node* node); Node* InsertChangeFloat64ToUint32(Node* node); Node* InsertChangeInt32ToFloat64(Node* node); Node* InsertChangeTaggedSignedToInt32(Node* node); Node* InsertChangeTaggedToFloat64(Node* node); Node* InsertChangeUint32ToFloat64(Node* node); Node* InsertConversion(Node* node, const Operator* op, Node* use_node); void InsertUnconditionalDeopt(Node* node, DeoptimizeReason reason); JSGraph* jsgraph() const { return jsgraph_; } Isolate* isolate() const { return isolate_; } Factory* factory() const { return isolate()->factory(); } SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); } MachineOperatorBuilder* machine() { return jsgraph()->machine(); } }; } // namespace compiler } // namespace internal } // namespace v8 #endif // V8_COMPILER_REPRESENTATION_CHANGE_H_