Commit 18f169d4 authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Propagate minus-zero truncation in representation inference.

This introduces a new truncation bit for truncation of minus-zero to zero.

At the moment it is only used to handle the limit cases of deopt, such as the
one in the Google maps workload (see simplified version below), where the -q
(which is desugared to q * -1.0) currently deoptimizes because the result would
produce minus zero. To handle this situation, we exploit the knowledge that
righthand side of + cannot be -0, so even if lefthand side was -0, the result
would still be 0 (so the + operation cannot distinguish between left hand side
0 and -0).

function f(q) {
  q -= 4;
  return (-q) + q;
}

f(10);
f(10);
%OptimizeFunctionOnNextCall(f);
f(4);

Review-Url: https://codereview.chromium.org/2734253002
Cr-Commit-Position: refs/heads/master@{#43661}
parent 99aaa69b
......@@ -27,9 +27,19 @@ const char* Truncation::description() const {
case TruncationKind::kWord64:
return "truncate-to-word64";
case TruncationKind::kFloat64:
return "truncate-to-float64";
switch (identify_zeros()) {
case kIdentifyZeros:
return "truncate-to-float64 (identify zeros)";
case kDistinguishZeros:
return "truncate-to-float64 (distinguish zeros)";
}
case TruncationKind::kAny:
return "no-truncation";
switch (identify_zeros()) {
case kIdentifyZeros:
return "no-truncation (but identify zeros)";
case kDistinguishZeros:
return "no-truncation (but distinguish zeros)";
}
}
UNREACHABLE();
return nullptr;
......@@ -38,10 +48,10 @@ const char* Truncation::description() const {
// Partial order for truncations:
//
// kWord64 kAny
// ^ ^
// \ |
// \ kFloat64 <--+
// kWord64 kAny <-------+
// ^ ^ |
// \ | |
// \ kFloat64 |
// \ ^ |
// \ / |
// kWord32 kBool
......@@ -52,6 +62,8 @@ const char* Truncation::description() const {
// \ /
// \ /
// kNone
//
// TODO(jarin) We might consider making kBool < kFloat64.
// static
Truncation::TruncationKind Truncation::Generalize(TruncationKind rep1,
......@@ -73,6 +85,15 @@ Truncation::TruncationKind Truncation::Generalize(TruncationKind rep1,
return TruncationKind::kNone;
}
// static
IdentifyZeros Truncation::GeneralizeIdentifyZeros(IdentifyZeros i1,
IdentifyZeros i2) {
if (i1 == i2) {
return i1;
} else {
return kDistinguishZeros;
}
}
// static
bool Truncation::LessGeneral(TruncationKind rep1, TruncationKind rep2) {
......@@ -96,6 +117,10 @@ bool Truncation::LessGeneral(TruncationKind rep1, TruncationKind rep2) {
return false;
}
// static
bool Truncation::LessGeneralIdentifyZeros(IdentifyZeros i1, IdentifyZeros i2) {
return i1 == i2 || i1 == kIdentifyZeros;
}
namespace {
......
......@@ -12,18 +12,34 @@ namespace v8 {
namespace internal {
namespace compiler {
enum IdentifyZeros { kIdentifyZeros, kDistinguishZeros };
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 Float64() { return Truncation(TruncationKind::kFloat64); }
static Truncation Any() { return Truncation(TruncationKind::kAny); }
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()));
return Truncation(
Generalize(t1.kind(), t2.kind()),
GeneralizeIdentifyZeros(t1.identify_zeros(), t2.identify_zeros()));
}
// Queries.
......@@ -45,17 +61,25 @@ class Truncation final {
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(); }
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());
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,
......@@ -66,13 +90,21 @@ class Truncation final {
kAny
};
explicit Truncation(TruncationKind kind) : kind_(kind) {}
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 {
......@@ -119,13 +151,10 @@ inline std::ostream& operator<<(std::ostream& os, TypeCheckKind type_check) {
class UseInfo {
public:
UseInfo(MachineRepresentation representation, Truncation truncation,
TypeCheckKind type_check = TypeCheckKind::kNone,
CheckForMinusZeroMode minus_zero_check =
CheckForMinusZeroMode::kCheckForMinusZero)
TypeCheckKind type_check = TypeCheckKind::kNone)
: representation_(representation),
truncation_(truncation),
type_check_(type_check),
minus_zero_check_(minus_zero_check) {}
type_check_(type_check) {}
static UseInfo TruncatingWord32() {
return UseInfo(MachineRepresentation::kWord32, Truncation::Word32());
}
......@@ -163,17 +192,14 @@ class UseInfo {
return UseInfo(MachineRepresentation::kTaggedSigned, Truncation::Any(),
TypeCheckKind::kSignedSmall);
}
static UseInfo CheckedSignedSmallAsWord32(
CheckForMinusZeroMode minus_zero_mode =
CheckForMinusZeroMode::kCheckForMinusZero) {
return UseInfo(MachineRepresentation::kWord32, Truncation::Any(),
TypeCheckKind::kSignedSmall, minus_zero_mode);
static UseInfo CheckedSignedSmallAsWord32(IdentifyZeros identify_zeros) {
return UseInfo(MachineRepresentation::kWord32,
Truncation::Any(identify_zeros),
TypeCheckKind::kSignedSmall);
}
static UseInfo CheckedSigned32AsWord32(
CheckForMinusZeroMode minus_zero_mode =
CheckForMinusZeroMode::kCheckForMinusZero) {
static UseInfo CheckedSigned32AsWord32(IdentifyZeros identify_zeros) {
return UseInfo(MachineRepresentation::kWord32, Truncation::Any(),
TypeCheckKind::kSigned32, minus_zero_mode);
TypeCheckKind::kSigned32);
}
static UseInfo CheckedNumberAsFloat64() {
return UseInfo(MachineRepresentation::kFloat64, Truncation::Float64(),
......@@ -208,14 +234,16 @@ class UseInfo {
MachineRepresentation representation() const { return representation_; }
Truncation truncation() const { return truncation_; }
TypeCheckKind type_check() const { return type_check_; }
CheckForMinusZeroMode minus_zero_check() const { return minus_zero_check_; }
CheckForMinusZeroMode minus_zero_check() const {
return truncation().IdentifiesZeroAndMinusZero()
? CheckForMinusZeroMode::kDontCheckForMinusZero
: CheckForMinusZeroMode::kCheckForMinusZero;
}
private:
MachineRepresentation representation_;
Truncation truncation_;
TypeCheckKind type_check_;
// TODO(jarin) Integrate with truncations.
CheckForMinusZeroMode minus_zero_check_;
};
// Contains logic related to changing the representation of values for constants
......
......@@ -88,13 +88,13 @@ MachineRepresentation MachineRepresentationFromArrayType(
}
UseInfo CheckedUseInfoAsWord32FromHint(
NumberOperationHint hint, CheckForMinusZeroMode minus_zero_mode =
CheckForMinusZeroMode::kCheckForMinusZero) {
NumberOperationHint hint,
IdentifyZeros identify_zeros = kDistinguishZeros) {
switch (hint) {
case NumberOperationHint::kSignedSmall:
return UseInfo::CheckedSignedSmallAsWord32(minus_zero_mode);
return UseInfo::CheckedSignedSmallAsWord32(identify_zeros);
case NumberOperationHint::kSigned32:
return UseInfo::CheckedSigned32AsWord32(minus_zero_mode);
return UseInfo::CheckedSigned32AsWord32(identify_zeros);
case NumberOperationHint::kNumber:
return UseInfo::CheckedNumberAsWord32();
case NumberOperationHint::kNumberOrOddball:
......@@ -1167,7 +1167,7 @@ class RepresentationSelector {
// If one of the inputs is positive and/or truncation is being applied,
// there is no need to return -0.
CheckForMinusZeroMode mz_mode =
truncation.IsUsedAsWord32() ||
truncation.IdentifiesZeroAndMinusZero() ||
(input0_type->Is(Type::OrderedNumber()) &&
input0_type->Min() > 0) ||
(input1_type->Is(Type::OrderedNumber()) &&
......@@ -1226,13 +1226,23 @@ class RepresentationSelector {
VisitBinop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32, Type::Signed32());
} else {
UseInfo left_use = CheckedUseInfoAsWord32FromHint(hint);
// If the output's truncation is identify-zeros, we can pass it
// along. Moreover, if the operation is addition and we know the
// right-hand side is not minus zero, we do not have to distinguish
// between 0 and -0.
IdentifyZeros left_identify_zeros = truncation.identify_zeros();
if (node->opcode() == IrOpcode::kSpeculativeNumberAdd &&
!right_feedback_type->Maybe(Type::MinusZero())) {
left_identify_zeros = kIdentifyZeros;
}
UseInfo left_use =
CheckedUseInfoAsWord32FromHint(hint, left_identify_zeros);
// For CheckedInt32Add and CheckedInt32Sub, we don't need to do
// a minus zero check for the right hand side, since we already
// know that the left hand side is a proper Signed32 value,
// potentially guarded by a check.
UseInfo right_use = CheckedUseInfoAsWord32FromHint(
hint, CheckForMinusZeroMode::kDontCheckForMinusZero);
UseInfo right_use =
CheckedUseInfoAsWord32FromHint(hint, kIdentifyZeros);
VisitBinop(node, left_use, right_use, MachineRepresentation::kWord32,
Type::Signed32());
}
......@@ -2294,7 +2304,7 @@ class RepresentationSelector {
DeferReplacement(node, node->InputAt(0));
}
} else {
VisitBinop(node, UseInfo::CheckedSigned32AsWord32(),
VisitBinop(node, UseInfo::CheckedSigned32AsWord32(kIdentifyZeros),
UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
}
......@@ -2360,7 +2370,8 @@ class RepresentationSelector {
}
case IrOpcode::kCheckSmi: {
if (SmiValuesAre32Bits() && truncation.IsUsedAsWord32()) {
VisitUnop(node, UseInfo::CheckedSignedSmallAsWord32(),
VisitUnop(node,
UseInfo::CheckedSignedSmallAsWord32(kDistinguishZeros),
MachineRepresentation::kWord32);
} else {
VisitUnop(node, UseInfo::CheckedSignedSmallAsTaggedSigned(),
......
......@@ -445,20 +445,16 @@ static void TestMinusZeroCheck(IrOpcode::Value expected, Type* from_type) {
RepresentationChangerTester r;
CheckChange(expected, MachineRepresentation::kFloat64, from_type,
UseInfo::CheckedSignedSmallAsWord32(
CheckForMinusZeroMode::kCheckForMinusZero));
UseInfo::CheckedSignedSmallAsWord32(kDistinguishZeros));
CheckChange(expected, MachineRepresentation::kFloat64, from_type,
UseInfo::CheckedSignedSmallAsWord32(
CheckForMinusZeroMode::kDontCheckForMinusZero));
UseInfo::CheckedSignedSmallAsWord32(kIdentifyZeros));
CheckChange(expected, MachineRepresentation::kFloat64, from_type,
UseInfo::CheckedSigned32AsWord32(
CheckForMinusZeroMode::kCheckForMinusZero));
UseInfo::CheckedSigned32AsWord32(kDistinguishZeros));
CheckChange(expected, MachineRepresentation::kFloat64, from_type,
UseInfo::CheckedSigned32AsWord32(
CheckForMinusZeroMode::kDontCheckForMinusZero));
UseInfo::CheckedSigned32AsWord32(kDistinguishZeros));
}
TEST(MinusZeroCheck) {
......
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