Commit 9564ffe9 authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Replace information about uses by explicit truncation in representation selection.

This change replaces the bitwise masking of uses by storing the most general truncation for all uses.

Review URL: https://codereview.chromium.org/1464763003

Cr-Commit-Position: refs/heads/master@{#32248}
parent 47e1ab78
......@@ -16,10 +16,137 @@ namespace v8 {
namespace internal {
namespace compiler {
class Truncation final {
public:
// Constructors.
static Truncation None() { return Truncation(TruncationKind::kNone); }
static Truncation Bool() { return Truncation(TruncationKind::kBool); }
static Truncation Word32() { return Truncation(TruncationKind::kWord32); }
static Truncation Word64() { return Truncation(TruncationKind::kWord64); }
static Truncation Float32() { return Truncation(TruncationKind::kFloat32); }
static Truncation Float64() { return Truncation(TruncationKind::kFloat64); }
static Truncation Any() { return Truncation(TruncationKind::kAny); }
static Truncation Generalize(Truncation t1, Truncation t2) {
return Truncation(Generalize(t1.kind(), t2.kind()));
}
// Queries.
bool TruncatesToWord32() const {
return LessGeneral(kind_, TruncationKind::kWord32);
}
bool TruncatesNaNToZero() {
return LessGeneral(kind_, TruncationKind::kWord32) ||
LessGeneral(kind_, TruncationKind::kBool);
}
bool TruncatesUndefinedToZeroOrNaN() {
return LessGeneral(kind_, TruncationKind::kFloat64) ||
LessGeneral(kind_, TruncationKind::kWord64);
}
// Operators.
bool operator==(Truncation other) const { return kind() == other.kind(); }
bool operator!=(Truncation other) const { return !(*this == other); }
// Debug utilities.
const char* description() {
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;
}
private:
enum class TruncationKind : uint8_t {
kNone,
kBool,
kWord32,
kWord64,
kFloat32,
kFloat64,
kAny
};
explicit Truncation(TruncationKind kind) : kind_(kind) {}
TruncationKind kind() const { return kind_; }
TruncationKind kind_;
// Partial order for truncations:
//
// kWord64 kAny
// ^ ^
// \ |
// \ kFloat64 <--+
// \ ^ ^ |
// \ / | |
// kWord32 kFloat32 kBool
// ^ ^ ^
// \ | /
// \ | /
// \ | /
// \ | /
// \ | /
// kNone
static TruncationKind 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 representations");
return TruncationKind::kNone;
}
static bool 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;
}
};
// 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 {
class RepresentationChanger final {
public:
RepresentationChanger(JSGraph* jsgraph, Isolate* isolate)
: jsgraph_(jsgraph),
......@@ -32,34 +159,40 @@ class RepresentationChanger {
return (type & (kRepWord8 | kRepWord16 | kRepWord32)) != 0;
}
// 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, MachineTypeUnion output_type,
MachineTypeUnion use_type) {
MachineTypeUnion use_rep,
Truncation truncation = Truncation::None()) {
DCHECK((use_rep & kRepMask) == use_rep);
if (!base::bits::IsPowerOfTwo32(output_type & kRepMask)) {
// There should be only one output representation.
return TypeError(node, output_type, use_type);
return TypeError(node, output_type, use_rep);
}
if ((use_type & kRepMask) == (output_type & kRepMask)) {
if (use_rep == (output_type & kRepMask)) {
// Representations are the same. That's a no-op.
return node;
}
if (IsWord(use_type) && IsWord(output_type)) {
if (IsWord(use_rep) && IsWord(output_type)) {
// 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) {
if (use_rep & kRepTagged) {
return GetTaggedRepresentationFor(node, output_type);
} else if (use_type & kRepFloat32) {
return GetFloat32RepresentationFor(node, output_type, use_type);
} else if (use_type & kRepFloat64) {
return GetFloat64RepresentationFor(node, output_type, use_type);
} else if (use_type & kRepBit) {
} else if (use_rep & kRepFloat32) {
return GetFloat32RepresentationFor(node, output_type, truncation);
} else if (use_rep & kRepFloat64) {
return GetFloat64RepresentationFor(node, output_type, truncation);
} else if (use_rep & kRepBit) {
return GetBitRepresentationFor(node, output_type);
} else if (IsWord(use_type)) {
} else if (IsWord(use_rep)) {
return GetWord32RepresentationFor(node, output_type);
} else if (use_type & kRepWord64) {
} else if (use_rep & kRepWord64) {
return GetWord64RepresentationFor(node, output_type);
} else {
return node;
......@@ -116,7 +249,7 @@ class RepresentationChanger {
}
Node* GetFloat32RepresentationFor(Node* node, MachineTypeUnion output_type,
MachineTypeUnion truncation) {
Truncation truncation) {
// Eagerly fold representation changes for constants.
switch (node->opcode()) {
case IrOpcode::kFloat64Constant:
......@@ -146,8 +279,7 @@ class RepresentationChanger {
} 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 & kTypeInt32 ||
!(truncation & ~(kTypeInt32 | kTypeUint32 | kRepMask)));
DCHECK(output_type & kTypeInt32 || truncation.TruncatesToWord32());
op = machine()->ChangeInt32ToFloat64();
}
// int32 -> float64 -> float32
......@@ -167,7 +299,7 @@ class RepresentationChanger {
}
Node* GetFloat64RepresentationFor(Node* node, MachineTypeUnion output_type,
MachineTypeUnion use_type) {
Truncation truncation) {
// Eagerly fold representation changes for constants.
switch (node->opcode()) {
case IrOpcode::kNumberConstant:
......@@ -197,8 +329,7 @@ class RepresentationChanger {
} 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 & kTypeInt32 ||
!(use_type & ~(kTypeInt32 | kTypeUint32 | kRepMask)));
DCHECK(output_type & kTypeInt32 || truncation.TruncatesToWord32());
op = machine()->ChangeInt32ToFloat64();
}
} else if (output_type & kRepTagged) {
......
This diff is collapsed.
......@@ -452,11 +452,10 @@ TEST(SingleChanges) {
TEST(SignednessInWord32) {
RepresentationChangerTester r;
// TODO(titzer): assume that uses of a word32 without a sign mean kTypeInt32.
CheckChange(IrOpcode::kChangeTaggedToInt32, kRepTagged | kTypeInt32,
kRepWord32 | kTypeInt32);
kRepWord32);
CheckChange(IrOpcode::kChangeTaggedToUint32, kRepTagged | kTypeUint32,
kRepWord32 | kTypeUint32);
kRepWord32);
CheckChange(IrOpcode::kChangeInt32ToFloat64, kRepWord32, kRepFloat64);
CheckChange(IrOpcode::kChangeFloat64ToInt32, kRepFloat64 | kTypeInt32,
kRepWord32);
......@@ -480,7 +479,6 @@ TEST(Nops) {
// 32-bit floats.
r.CheckNop(kRepFloat32, kRepFloat32);
r.CheckNop(kRepFloat32 | kTypeNumber, kRepFloat32);
r.CheckNop(kRepFloat32, kRepFloat32 | kTypeNumber);
// 32-bit words can be used as smaller word sizes and vice versa, because
// loads from memory implicitly sign or zero extend the value to the
......@@ -508,39 +506,29 @@ TEST(TypeErrors) {
// Wordish cannot be implicitly converted to/from comparison conditions.
r.CheckTypeError(kRepWord8, kRepBit);
r.CheckTypeError(kRepWord8, kRepBit | kTypeBool);
r.CheckTypeError(kRepWord16, kRepBit);
r.CheckTypeError(kRepWord16, kRepBit | kTypeBool);
r.CheckTypeError(kRepWord32, kRepBit);
r.CheckTypeError(kRepWord32, kRepBit | kTypeBool);
r.CheckTypeError(kRepWord64, kRepBit);
r.CheckTypeError(kRepWord64, kRepBit | kTypeBool);
// Floats cannot be implicitly converted to/from comparison conditions.
r.CheckTypeError(kRepFloat64, kRepBit);
r.CheckTypeError(kRepFloat64, kRepBit | kTypeBool);
r.CheckTypeError(kRepBit, kRepFloat64);
r.CheckTypeError(kRepBit | kTypeBool, kRepFloat64);
// Floats cannot be implicitly converted to/from comparison conditions.
r.CheckTypeError(kRepFloat32, kRepBit);
r.CheckTypeError(kRepFloat32, kRepBit | kTypeBool);
r.CheckTypeError(kRepBit, kRepFloat32);
r.CheckTypeError(kRepBit | kTypeBool, kRepFloat32);
// Word64 is internal and shouldn't be implicitly converted.
r.CheckTypeError(kRepWord64, kRepTagged | kTypeBool);
r.CheckTypeError(kRepWord64, kRepTagged);
r.CheckTypeError(kRepWord64, kRepTagged | kTypeBool);
r.CheckTypeError(kRepTagged, kRepWord64);
r.CheckTypeError(kRepTagged | kTypeBool, kRepWord64);
// Word64 / Word32 shouldn't be implicitly converted.
r.CheckTypeError(kRepWord64, kRepWord32);
r.CheckTypeError(kRepWord32, kRepWord64);
r.CheckTypeError(kRepWord64, kRepWord32 | kTypeInt32);
r.CheckTypeError(kRepWord32 | kTypeInt32, kRepWord64);
r.CheckTypeError(kRepWord64, kRepWord32 | kTypeUint32);
r.CheckTypeError(kRepWord32 | kTypeUint32, kRepWord64);
for (size_t i = 0; i < arraysize(all_reps); i++) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment