Commit 6a08d18d authored by Jaroslav Sevcik's avatar Jaroslav Sevcik Committed by Commit Bot

[turbofan] Better type and truncation for speculative additive ops.

This helps with patterns such as

((a[i] + n) + m) | 0

where we know n and m are small integers, and a[i] is a holey smi
array where we have never read a hole so far.

In that case, we still perform the additions with overflow checks
since we currently only propagate/use the truncation if the operation
outcome is in the safe-integer range (without taking feedback into
account).  The problem here is that both 'n + a[i]' and '(n + a[i]) +
m' have type Union(Range(..., ...), NaN), even though the NaN will
never pass the Smi check on a[i].

This CL changes restricts the static type of 
SpeculativeSafeInteger(Add|Subtract) to the safe integer range. 
This is safe because we will always either truncate or use the feedback
(i.e., deopt if the inputs are not Signed32).  In either case, the 
result will always be in safe-integer range.

As a result, we will perform the second addition without
overflow check. Getting rid of the overflow check on the first
is done in a separate CL.

Bug: v8:5267,v8:6764
Change-Id: I27dba0fda832fc1f04477db6dd3495d5b4b2bd0b
Reviewed-on: https://chromium-review.googlesource.com/634903
Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47763}
parent d667bf4a
...@@ -599,6 +599,26 @@ Type* OperationTyper::NumberSubtract(Type* lhs, Type* rhs) { ...@@ -599,6 +599,26 @@ Type* OperationTyper::NumberSubtract(Type* lhs, Type* rhs) {
return type; return type;
} }
Type* OperationTyper::SpeculativeSafeIntegerAdd(Type* lhs, Type* rhs) {
Type* result = SpeculativeNumberAdd(lhs, rhs);
// If we have a Smi or Int32 feedback, the representation selection will
// either truncate or it will check the inputs (i.e., deopt if not int32).
// In either case the result will be in the safe integer range, so we
// can bake in the type here. This needs to be in sync with
// SimplifiedLowering::VisitSpeculativeAdditiveOp.
return Type::Intersect(result, cache_.kSafeInteger, zone());
}
Type* OperationTyper::SpeculativeSafeIntegerSubtract(Type* lhs, Type* rhs) {
Type* result = SpeculativeNumberSubtract(lhs, rhs);
// If we have a Smi or Int32 feedback, the representation selection will
// either truncate or it will check the inputs (i.e., deopt if not int32).
// In either case the result will be in the safe integer range, so we
// can bake in the type here. This needs to be in sync with
// SimplifiedLowering::VisitSpeculativeAdditiveOp.
return result = Type::Intersect(result, cache_.kSafeInteger, zone());
}
Type* OperationTyper::NumberMultiply(Type* lhs, Type* rhs) { Type* OperationTyper::NumberMultiply(Type* lhs, Type* rhs) {
DCHECK(lhs->Is(Type::Number())); DCHECK(lhs->Is(Type::Number()));
DCHECK(rhs->Is(Type::Number())); DCHECK(rhs->Is(Type::Number()));
...@@ -987,18 +1007,6 @@ SPECULATIVE_NUMBER_BINOP(NumberShiftRight) ...@@ -987,18 +1007,6 @@ SPECULATIVE_NUMBER_BINOP(NumberShiftRight)
SPECULATIVE_NUMBER_BINOP(NumberShiftRightLogical) SPECULATIVE_NUMBER_BINOP(NumberShiftRightLogical)
#undef SPECULATIVE_NUMBER_BINOP #undef SPECULATIVE_NUMBER_BINOP
Type* OperationTyper::SpeculativeSafeIntegerAdd(Type* lhs, Type* rhs) {
lhs = SpeculativeToNumber(lhs);
rhs = SpeculativeToNumber(rhs);
return NumberAdd(lhs, rhs);
}
Type* OperationTyper::SpeculativeSafeIntegerSubtract(Type* lhs, Type* rhs) {
lhs = SpeculativeToNumber(lhs);
rhs = SpeculativeToNumber(rhs);
return NumberSubtract(lhs, rhs);
}
Type* OperationTyper::SpeculativeToNumber(Type* type) { Type* OperationTyper::SpeculativeToNumber(Type* type) {
return ToNumber(Type::Intersect(type, Type::NumberOrOddball(), zone())); return ToNumber(Type::Intersect(type, Type::NumberOrOddball(), zone()));
} }
......
...@@ -1262,78 +1262,77 @@ class RepresentationSelector { ...@@ -1262,78 +1262,77 @@ class RepresentationSelector {
void VisitSpeculativeIntegerAdditiveOp(Node* node, Truncation truncation, void VisitSpeculativeIntegerAdditiveOp(Node* node, Truncation truncation,
SimplifiedLowering* lowering) { SimplifiedLowering* lowering) {
Type* left_upper = GetUpperBound(node->InputAt(0));
Type* right_upper = GetUpperBound(node->InputAt(1));
// Only eliminate eliminate the node if the ToNumber conversion cannot // Only eliminate eliminate the node if the ToNumber conversion cannot
// cause any observable side-effect and if we know for sure that it // cause any observable side-effect and if we know for sure that it
// is a number addition (we must exclude strings). // is a number addition (we must exclude strings).
if (BothInputsAre(node, Type::NumberOrOddball())) { if (left_upper->Is(Type::NumberOrOddball()) &&
right_upper->Is(Type::NumberOrOddball())) {
if (truncation.IsUnused()) return VisitUnused(node); if (truncation.IsUnused()) return VisitUnused(node);
} }
if (BothInputsAre(node, type_cache_.kAdditiveSafeIntegerOrMinusZero) && if (left_upper->Is(type_cache_.kAdditiveSafeIntegerOrMinusZero) &&
(GetUpperBound(node)->Is(Type::Signed32()) || right_upper->Is(type_cache_.kAdditiveSafeIntegerOrMinusZero)) {
GetUpperBound(node)->Is(Type::Unsigned32()) || // If we know how to interpret the result or if the users only care
truncation.IsUsedAsWord32())) { // about the low 32-bits, we can truncate to Word32 do a wrapping
// => Int32Add/Sub // addition.
VisitWord32TruncatingBinop(node); if (GetUpperBound(node)->Is(Type::Signed32()) ||
if (lower()) ChangeToPureOp(node, Int32Op(node)); GetUpperBound(node)->Is(Type::Unsigned32()) ||
return; truncation.IsUsedAsWord32()) {
// => Int32Add/Sub
VisitWord32TruncatingBinop(node);
if (lower()) ChangeToPureOp(node, Int32Op(node));
return;
}
} }
// Try to use type feedback. // Try to use type feedback.
NumberOperationHint hint = NumberOperationHintOf(node->op()); NumberOperationHint hint = NumberOperationHintOf(node->op());
if (hint == NumberOperationHint::kSignedSmall || DCHECK(hint == NumberOperationHint::kSignedSmall ||
hint == NumberOperationHint::kSigned32) { hint == NumberOperationHint::kSigned32);
Type* left_feedback_type = TypeOf(node->InputAt(0));
Type* right_feedback_type = TypeOf(node->InputAt(1)); Type* left_feedback_type = TypeOf(node->InputAt(0));
// Handle the case when no int32 checks on inputs are necessary (but Type* right_feedback_type = TypeOf(node->InputAt(1));
// an overflow check is needed on the output). // Handle the case when no int32 checks on inputs are necessary (but
// TODO(jarin) We should not look at the upper bound because the typer // an overflow check is needed on the output). Note that we do not
// could have already baked in some feedback into the upper bound. // have to do any check if at most one side can be minus zero.
if (BothInputsAre(node, Type::Signed32()) || if (left_upper->Is(Type::Signed32OrMinusZero()) &&
(BothInputsAre(node, Type::Signed32OrMinusZero()) && right_upper->Is(Type::Signed32OrMinusZero()) &&
GetUpperBound(node)->Is(type_cache_.kSafeInteger))) { (left_upper->Is(Type::Signed32()) ||
VisitBinop(node, UseInfo::TruncatingWord32(), right_upper->Is(Type::Signed32()))) {
MachineRepresentation::kWord32, Type::Signed32()); VisitBinop(node, UseInfo::TruncatingWord32(),
} else { MachineRepresentation::kWord32, Type::Signed32());
// If the output's truncation is identify-zeros, we can pass it } else {
// along. Moreover, if the operation is addition and we know the // If the output's truncation is identify-zeros, we can pass it
// right-hand side is not minus zero, we do not have to distinguish // along. Moreover, if the operation is addition and we know the
// between 0 and -0. // right-hand side is not minus zero, we do not have to distinguish
IdentifyZeros left_identify_zeros = truncation.identify_zeros(); // between 0 and -0.
if (node->opcode() == IrOpcode::kSpeculativeSafeIntegerAdd && IdentifyZeros left_identify_zeros = truncation.identify_zeros();
!right_feedback_type->Maybe(Type::MinusZero())) { if (node->opcode() == IrOpcode::kSpeculativeSafeIntegerAdd &&
left_identify_zeros = kIdentifyZeros; !right_feedback_type->Maybe(Type::MinusZero())) {
} left_identify_zeros = kIdentifyZeros;
UseInfo left_use = }
CheckedUseInfoAsWord32FromHint(hint, left_identify_zeros); UseInfo left_use =
// For CheckedInt32Add and CheckedInt32Sub, we don't need to do CheckedUseInfoAsWord32FromHint(hint, left_identify_zeros);
// a minus zero check for the right hand side, since we already // For CheckedInt32Add and CheckedInt32Sub, we don't need to do
// know that the left hand side is a proper Signed32 value, // a minus zero check for the right hand side, since we already
// potentially guarded by a check. // know that the left hand side is a proper Signed32 value,
UseInfo right_use = // potentially guarded by a check.
CheckedUseInfoAsWord32FromHint(hint, kIdentifyZeros); UseInfo right_use = CheckedUseInfoAsWord32FromHint(hint, kIdentifyZeros);
VisitBinop(node, left_use, right_use, MachineRepresentation::kWord32, VisitBinop(node, left_use, right_use, MachineRepresentation::kWord32,
Type::Signed32()); Type::Signed32());
}
if (lower()) {
if (truncation.IsUsedAsWord32() ||
!CanOverflowSigned32(node->op(), left_feedback_type,
right_feedback_type, graph_zone())) {
ChangeToPureOp(node, Int32Op(node));
} else {
ChangeToInt32OverflowOp(node);
}
}
return;
} }
// default case => Float64Add/Sub
VisitBinop(node, UseInfo::CheckedNumberOrOddballAsFloat64(),
MachineRepresentation::kFloat64, Type::Number());
if (lower()) { if (lower()) {
ChangeToPureOp(node, Float64Op(node)); if (truncation.IsUsedAsWord32() ||
!CanOverflowSigned32(node->op(), left_feedback_type,
right_feedback_type, graph_zone())) {
ChangeToPureOp(node, Int32Op(node));
} else {
ChangeToInt32OverflowOp(node);
}
} }
return; return;
} }
......
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