Commit 17a7f583 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Improve typing rule for modulus.

Infer a more precise type even in case where NaN and/or -0 is a possible
outcome of the operation, and use this more precise type to improve code
generation for the modulus itself by trying harder to stick to Word32
operations instead of going to Float64, and also optimize the pattern
where we compare the output of x % y to some non-zero integer constant
K, in which case we can truncate the output of x % y to Word32 if the
type of x % y is Signed32/Unsigned32 \/ NaN \/ MinusZero, as NaN and
MinusZero will both be truncated to zero, which cannot match the non
zero constant K.

R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2202413002
Cr-Commit-Position: refs/heads/master@{#38267}
parent 0c76b0ae
......@@ -204,36 +204,6 @@ Type* OperationTyper::SubtractRanger(RangeType* lhs, RangeType* rhs) {
// [m, +inf] - [-inf, n] = [-inf, +inf] \/ NaN
}
Type* OperationTyper::ModulusRanger(RangeType* lhs, RangeType* rhs) {
double lmin = lhs->Min();
double lmax = lhs->Max();
double rmin = rhs->Min();
double rmax = rhs->Max();
double labs = std::max(std::abs(lmin), std::abs(lmax));
double rabs = std::max(std::abs(rmin), std::abs(rmax)) - 1;
double abs = std::min(labs, rabs);
bool maybe_minus_zero = false;
double omin = 0;
double omax = 0;
if (lmin >= 0) { // {lhs} positive.
omin = 0;
omax = abs;
} else if (lmax <= 0) { // {lhs} negative.
omin = 0 - abs;
omax = 0;
maybe_minus_zero = true;
} else {
omin = 0 - abs;
omax = abs;
maybe_minus_zero = true;
}
Type* result = Type::Range(omin, omax, zone());
if (maybe_minus_zero) result = Type::Union(result, Type::MinusZero(), zone());
return result;
}
Type* OperationTyper::MultiplyRanger(Type* lhs, Type* rhs) {
double results[4];
double lmin = lhs->AsRange()->Min();
......@@ -373,24 +343,67 @@ Type* OperationTyper::NumberModulus(Type* lhs, Type* rhs) {
DCHECK(lhs->Is(Type::Number()));
DCHECK(rhs->Is(Type::Number()));
if (!lhs->IsInhabited() || !rhs->IsInhabited()) {
return Type::None();
}
// Modulus can yield NaN if either {lhs} or {rhs} are NaN, or
// {lhs} is not finite, or the {rhs} is a zero value.
bool maybe_nan = lhs->Maybe(Type::NaN()) || rhs->Maybe(cache_.kZeroish) ||
lhs->Min() == -V8_INFINITY || lhs->Max() == +V8_INFINITY;
if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
if (lhs->Maybe(Type::NaN()) || rhs->Maybe(cache_.kZeroish) ||
lhs->Min() == -V8_INFINITY || lhs->Max() == +V8_INFINITY) {
// Result maybe NaN.
return Type::Number();
// Deal with -0 inputs, only the signbit of {lhs} matters for the result.
bool maybe_minuszero = false;
if (lhs->Maybe(Type::MinusZero())) {
maybe_minuszero = true;
lhs = Type::Union(lhs, cache_.kSingletonZero, zone());
}
lhs = Rangify(lhs);
rhs = Rangify(rhs);
if (lhs->IsRange() && rhs->IsRange()) {
return ModulusRanger(lhs->AsRange(), rhs->AsRange());
if (rhs->Maybe(Type::MinusZero())) {
rhs = Type::Union(rhs, cache_.kSingletonZero, zone());
}
// Rule out NaN and -0, and check what we can do with the remaining type info.
Type* type = Type::None();
lhs = Type::Intersect(lhs, Type::PlainNumber(), zone());
rhs = Type::Intersect(rhs, Type::PlainNumber(), zone());
// We can only derive a meaningful type if both {lhs} and {rhs} are inhabited,
// and the {rhs} is not 0, otherwise the result is NaN independent of {lhs}.
if (lhs->IsInhabited() && !rhs->Is(cache_.kSingletonZero)) {
// Determine the bounds of {lhs} and {rhs}.
double const lmin = lhs->Min();
double const lmax = lhs->Max();
double const rmin = rhs->Min();
double const rmax = rhs->Max();
// The sign of the result is the sign of the {lhs}.
if (lmin < 0.0) maybe_minuszero = true;
// For integer inputs {lhs} and {rhs} we can infer a precise type.
if (lhs->Is(cache_.kInteger) && rhs->Is(cache_.kInteger)) {
double labs = std::max(std::abs(lmin), std::abs(lmax));
double rabs = std::max(std::abs(rmin), std::abs(rmax)) - 1;
double abs = std::min(labs, rabs);
double min = 0.0, max = 0.0;
if (lmin >= 0.0) {
// {lhs} positive.
min = 0.0;
max = abs;
} else if (lmax <= 0.0) {
// {lhs} negative.
min = 0.0 - abs;
max = 0.0;
} else {
// {lhs} positive or negative.
min = 0.0 - abs;
max = abs;
}
type = Type::Range(min, max, zone());
} else {
type = Type::PlainNumber();
}
}
return Type::OrderedNumber();
// Take into account the -0 and NaN information computed earlier.
if (maybe_minuszero) type = Type::Union(type, Type::MinusZero(), zone());
if (maybe_nan) type = Type::Union(type, Type::NaN(), zone());
return type;
}
Type* OperationTyper::NumberAbs(Type* type) {
......
......@@ -67,7 +67,6 @@ class OperationTyper {
double rhs_max);
Type* SubtractRanger(RangeType* lhs, RangeType* rhs);
Type* MultiplyRanger(Type* lhs, Type* rhs);
Type* ModulusRanger(RangeType* lhs, RangeType* rhs);
Zone* zone() { return zone_; }
......
......@@ -677,6 +677,12 @@ class RepresentationSelector {
GetUpperBound(node->InputAt(1))->Is(type);
}
bool OneInputCannotBe(Node* node, Type* type) {
DCHECK_EQ(2, node->op()->ValueInputCount());
return !GetUpperBound(node->InputAt(0))->Maybe(type) ||
!GetUpperBound(node->InputAt(1))->Maybe(type);
}
void ConvertInput(Node* node, int index, UseInfo use) {
Node* input = node->InputAt(index);
// In the change phase, insert a change before the use if necessary.
......@@ -1127,7 +1133,7 @@ class RepresentationSelector {
if (BothInputsAre(node, Type::PlainPrimitive())) {
if (truncation.IsUnused()) return VisitUnused(node);
}
if (BothInputsAre(node, type_cache_.kSigned32OrMinusZero) &&
if (BothInputsAre(node, Type::Signed32OrMinusZero()) &&
NodeProperties::GetType(node)->Is(Type::Signed32())) {
// int32 + int32 = int32 ==> signed Int32Add/Sub
VisitInt32Binop(node);
......@@ -1151,7 +1157,7 @@ class RepresentationSelector {
// Handle the case when no int32 checks on inputs are necessary
// (but an overflow check is needed on the output).
if (BothInputsAre(node, Type::Signed32()) ||
(BothInputsAre(node, type_cache_.kSigned32OrMinusZero) &&
(BothInputsAre(node, Type::Signed32OrMinusZero()) &&
NodeProperties::GetType(node)->Is(type_cache_.kSafeInteger))) {
// If both the inputs the feedback are int32, use the overflow op.
if (hint == BinaryOperationHints::kSignedSmall ||
......@@ -1290,7 +1296,35 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kNumberEqual:
case IrOpcode::kNumberEqual: {
Type* const lhs_type = TypeOf(node->InputAt(0));
Type* const rhs_type = TypeOf(node->InputAt(1));
// Number comparisons reduce to integer comparisons for integer inputs.
if ((lhs_type->Is(Type::Unsigned32()) &&
rhs_type->Is(Type::Unsigned32())) ||
(lhs_type->Is(Type::Unsigned32OrMinusZeroOrNaN()) &&
rhs_type->Is(Type::Unsigned32OrMinusZeroOrNaN()) &&
OneInputCannotBe(node, type_cache_.kZeroish))) {
// => unsigned Int32Cmp
VisitUint32Cmp(node);
if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node));
return;
}
if ((lhs_type->Is(Type::Signed32()) &&
rhs_type->Is(Type::Signed32())) ||
(lhs_type->Is(Type::Signed32OrMinusZeroOrNaN()) &&
rhs_type->Is(Type::Signed32OrMinusZeroOrNaN()) &&
OneInputCannotBe(node, type_cache_.kZeroish))) {
// => signed Int32Cmp
VisitInt32Cmp(node);
if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
return;
}
// => Float64Cmp
VisitFloat64Cmp(node);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
return;
}
case IrOpcode::kNumberLessThan:
case IrOpcode::kNumberLessThanOrEqual: {
// Number comparisons reduce to integer comparisons for integer inputs.
......@@ -1580,25 +1614,21 @@ class RepresentationSelector {
if (BothInputsAre(node, Type::PlainPrimitive())) {
if (truncation.IsUnused()) return VisitUnused(node);
}
if (BothInputsAreUnsigned32(node) && truncation.IsUsedAsWord32()) {
if (BothInputsAre(node, Type::Unsigned32OrMinusZeroOrNaN()) &&
(truncation.IsUsedAsWord32() ||
NodeProperties::GetType(node)->Is(Type::Unsigned32()))) {
// => unsigned Uint32Mod
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
return;
}
if (BothInputsAreSigned32(node)) {
if (NodeProperties::GetType(node)->Is(Type::Signed32())) {
// => signed Int32Mod
VisitInt32Binop(node);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
return;
}
if (truncation.IsUsedAsWord32()) {
// => signed Int32Mod
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
return;
}
if (BothInputsAre(node, Type::Signed32OrMinusZeroOrNaN()) &&
(truncation.IsUsedAsWord32() ||
NodeProperties::GetType(node)->Is(Type::Signed32()))) {
// => signed Int32Mod
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
return;
}
// Try to use type feedback.
......@@ -1644,6 +1674,30 @@ class RepresentationSelector {
return;
}
if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
TypeOf(node->InputAt(1))->Is(Type::Unsigned32()) &&
(truncation.IsUsedAsWord32() ||
NodeProperties::GetType(node)->Is(Type::Unsigned32()))) {
// We can only promise Float64 truncation here, as the decision is
// based on the feedback types of the inputs.
VisitBinop(node, UseInfo(MachineRepresentation::kWord32,
Truncation::Float64()),
MachineRepresentation::kWord32);
if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
return;
}
if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) &&
TypeOf(node->InputAt(1))->Is(Type::Signed32()) &&
(truncation.IsUsedAsWord32() ||
NodeProperties::GetType(node)->Is(Type::Signed32()))) {
// We can only promise Float64 truncation here, as the decision is
// based on the feedback types of the inputs.
VisitBinop(node, UseInfo(MachineRepresentation::kWord32,
Truncation::Float64()),
MachineRepresentation::kWord32);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
return;
}
// default case => Float64Mod
VisitBinop(node, UseInfo::CheckedNumberOrOddballAsFloat64(),
MachineRepresentation::kFloat64, Type::Number());
......@@ -1651,27 +1705,47 @@ class RepresentationSelector {
return;
}
case IrOpcode::kNumberModulus: {
if (BothInputsAreUnsigned32(node) && truncation.IsUsedAsWord32()) {
if (BothInputsAre(node, Type::Unsigned32OrMinusZeroOrNaN()) &&
(truncation.IsUsedAsWord32() ||
NodeProperties::GetType(node)->Is(Type::Unsigned32()))) {
// => unsigned Uint32Mod
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
return;
}
if (BothInputsAreSigned32(node)) {
if (NodeProperties::GetType(node)->Is(Type::Signed32())) {
// => signed Int32Mod
VisitInt32Binop(node);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
return;
}
if (truncation.IsUsedAsWord32()) {
// => signed Int32Mod
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
return;
}
if (BothInputsAre(node, Type::Signed32OrMinusZeroOrNaN()) &&
(truncation.IsUsedAsWord32() ||
NodeProperties::GetType(node)->Is(Type::Signed32()))) {
// => signed Int32Mod
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
return;
}
// => Float64Mod
if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
TypeOf(node->InputAt(1))->Is(Type::Unsigned32()) &&
(truncation.IsUsedAsWord32() ||
NodeProperties::GetType(node)->Is(Type::Unsigned32()))) {
// We can only promise Float64 truncation here, as the decision is
// based on the feedback types of the inputs.
VisitBinop(node, UseInfo(MachineRepresentation::kWord32,
Truncation::Float64()),
MachineRepresentation::kWord32);
if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
return;
}
if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) &&
TypeOf(node->InputAt(1))->Is(Type::Signed32()) &&
(truncation.IsUsedAsWord32() ||
NodeProperties::GetType(node)->Is(Type::Signed32()))) {
// We can only promise Float64 truncation here, as the decision is
// based on the feedback types of the inputs.
VisitBinop(node, UseInfo(MachineRepresentation::kWord32,
Truncation::Float64()),
MachineRepresentation::kWord32);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
return;
}
// default case => Float64Mod
VisitFloat64Binop(node);
if (lower()) ChangeToPureOp(node, Float64Op(node));
return;
......
......@@ -76,8 +76,6 @@ class TypeCache final {
Type* const kUntaggedUndefined =
Type::Intersect(Type::Undefined(), Type::Untagged(), zone());
Type* const kSigned32OrMinusZero =
Type::Union(Type::Signed32(), Type::MinusZero(), zone());
// Asm.js related types.
Type* const kAsmSigned = kInt32;
......
......@@ -201,39 +201,43 @@ namespace internal {
V(Function, 1u << 19 | REPRESENTATION(kTaggedPointer)) \
V(Internal, 1u << 20 | REPRESENTATION(kTagged | kUntagged)) \
\
V(Signed31, kUnsigned30 | kNegative31) \
V(Signed32, kSigned31 | kOtherUnsigned31 | kOtherSigned32) \
V(Negative32, kNegative31 | kOtherSigned32) \
V(Unsigned31, kUnsigned30 | kOtherUnsigned31) \
V(Unsigned32, kUnsigned30 | kOtherUnsigned31 | \
kOtherUnsigned32) \
V(Integral32, kSigned32 | kUnsigned32) \
V(PlainNumber, kIntegral32 | kOtherNumber) \
V(OrderedNumber, kPlainNumber | kMinusZero) \
V(MinusZeroOrNaN, kMinusZero | kNaN) \
V(Number, kOrderedNumber | kNaN) \
V(String, kInternalizedString | kOtherString) \
V(UniqueName, kSymbol | kInternalizedString) \
V(Name, kSymbol | kString) \
V(BooleanOrNumber, kBoolean | kNumber) \
V(BooleanOrNullOrUndefined, kBoolean | kNull | kUndefined) \
V(NullOrUndefined, kNull | kUndefined) \
V(Undetectable, kNullOrUndefined | kOtherUndetectable) \
V(NumberOrOddball, kNumber | kNullOrUndefined | kBoolean) \
V(NumberOrSimdOrString, kNumber | kSimd | kString) \
V(NumberOrString, kNumber | kString) \
V(NumberOrUndefined, kNumber | kUndefined) \
V(PlainPrimitive, kNumberOrString | kBoolean | kNullOrUndefined) \
V(Primitive, kSymbol | kSimd | kPlainPrimitive) \
V(DetectableReceiver, kFunction | kOtherObject | kProxy) \
V(Object, kFunction | kOtherObject | kOtherUndetectable) \
V(Receiver, kObject | kProxy) \
V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \
kReceiver) \
V(NonInternal, kPrimitive | kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \
V(Any, 0xfffffffeu)
V(Signed31, kUnsigned30 | kNegative31) \
V(Signed32, kSigned31 | kOtherUnsigned31 | kOtherSigned32) \
V(Signed32OrMinusZero, kSigned32 | kMinusZero) \
V(Signed32OrMinusZeroOrNaN, kSigned32 | kMinusZero | kNaN) \
V(Negative32, kNegative31 | kOtherSigned32) \
V(Unsigned31, kUnsigned30 | kOtherUnsigned31) \
V(Unsigned32, kUnsigned30 | kOtherUnsigned31 | \
kOtherUnsigned32) \
V(Unsigned32OrMinusZero, kUnsigned32 | kMinusZero) \
V(Unsigned32OrMinusZeroOrNaN, kUnsigned32 | kMinusZero | kNaN) \
V(Integral32, kSigned32 | kUnsigned32) \
V(PlainNumber, kIntegral32 | kOtherNumber) \
V(OrderedNumber, kPlainNumber | kMinusZero) \
V(MinusZeroOrNaN, kMinusZero | kNaN) \
V(Number, kOrderedNumber | kNaN) \
V(String, kInternalizedString | kOtherString) \
V(UniqueName, kSymbol | kInternalizedString) \
V(Name, kSymbol | kString) \
V(BooleanOrNumber, kBoolean | kNumber) \
V(BooleanOrNullOrUndefined, kBoolean | kNull | kUndefined) \
V(NullOrUndefined, kNull | kUndefined) \
V(Undetectable, kNullOrUndefined | kOtherUndetectable) \
V(NumberOrOddball, kNumber | kNullOrUndefined | kBoolean) \
V(NumberOrSimdOrString, kNumber | kSimd | kString) \
V(NumberOrString, kNumber | kString) \
V(NumberOrUndefined, kNumber | kUndefined) \
V(PlainPrimitive, kNumberOrString | kBoolean | kNullOrUndefined) \
V(Primitive, kSymbol | kSimd | kPlainPrimitive) \
V(DetectableReceiver, kFunction | kOtherObject | kProxy) \
V(Object, kFunction | kOtherObject | kOtherUndetectable) \
V(Receiver, kObject | kProxy) \
V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \
kReceiver) \
V(NonInternal, kPrimitive | kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \
V(Any, 0xfffffffeu)
// clang-format on
......
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