// 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_
|