[turbofan] Collect and use SignedSmall input feedback for Divide.

For Divide operations like

  r = a / b

where r has only truncated uses (i.e. only used in bitwise operations),
we used to generate a Float64Div unless we statically knew something
about a and b, even if a and b have always been integers so far.
Crankshaft was able to generate an integer division here, because
Fullcodegen collected feedback independently for inputs and outputs of
binary operations.

This adds new BinaryOperationFeedback::kSignedSmallInputs, which is used
specifically for Divide to state that we have seen only SignedSmall
inputs thus far, but the outputs weren't always in the SignedSmall
range.

The issue was discovered in a WebGL Triangulation library and reported
via https://twitter.com/mourner/status/895708603117518848 after Node
8.3.0 was released with I+TF.

R=jarin@chromium.org

Bug: v8:6698
Change-Id: I830e421a3bf91fc8fa3665cbb706bc13675a6d2b
Reviewed-on: https://chromium-review.googlesource.com/612063
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47302}
This commit is contained in:
Benedikt Meurer 2017-08-11 10:17:54 +02:00 committed by Commit Bot
parent f984eb1bbb
commit 622852e5a6
11 changed files with 48 additions and 18 deletions

View File

@ -599,6 +599,8 @@ struct JSOperatorGlobalCache final {
Name##Operator<BinaryOperationHint::kNone> k##Name##NoneOperator; \ Name##Operator<BinaryOperationHint::kNone> k##Name##NoneOperator; \
Name##Operator<BinaryOperationHint::kSignedSmall> \ Name##Operator<BinaryOperationHint::kSignedSmall> \
k##Name##SignedSmallOperator; \ k##Name##SignedSmallOperator; \
Name##Operator<BinaryOperationHint::kSignedSmallInputs> \
k##Name##SignedSmallInputsOperator; \
Name##Operator<BinaryOperationHint::kSigned32> k##Name##Signed32Operator; \ Name##Operator<BinaryOperationHint::kSigned32> k##Name##Signed32Operator; \
Name##Operator<BinaryOperationHint::kNumber> k##Name##NumberOperator; \ Name##Operator<BinaryOperationHint::kNumber> k##Name##NumberOperator; \
Name##Operator<BinaryOperationHint::kNumberOrOddball> \ Name##Operator<BinaryOperationHint::kNumberOrOddball> \
@ -652,6 +654,8 @@ CACHED_OP_LIST(CACHED_OP)
return &cache_.k##Name##NoneOperator; \ return &cache_.k##Name##NoneOperator; \
case BinaryOperationHint::kSignedSmall: \ case BinaryOperationHint::kSignedSmall: \
return &cache_.k##Name##SignedSmallOperator; \ return &cache_.k##Name##SignedSmallOperator; \
case BinaryOperationHint::kSignedSmallInputs: \
return &cache_.k##Name##SignedSmallInputsOperator; \
case BinaryOperationHint::kSigned32: \ case BinaryOperationHint::kSigned32: \
return &cache_.k##Name##Signed32Operator; \ return &cache_.k##Name##Signed32Operator; \
case BinaryOperationHint::kNumber: \ case BinaryOperationHint::kNumber: \

View File

@ -22,6 +22,9 @@ bool BinaryOperationHintToNumberOperationHint(
case BinaryOperationHint::kSignedSmall: case BinaryOperationHint::kSignedSmall:
*number_hint = NumberOperationHint::kSignedSmall; *number_hint = NumberOperationHint::kSignedSmall;
return true; return true;
case BinaryOperationHint::kSignedSmallInputs:
*number_hint = NumberOperationHint::kSignedSmallInputs;
return true;
case BinaryOperationHint::kSigned32: case BinaryOperationHint::kSigned32:
*number_hint = NumberOperationHint::kSigned32; *number_hint = NumberOperationHint::kSigned32;
return true; return true;

View File

@ -91,6 +91,7 @@ UseInfo CheckedUseInfoAsWord32FromHint(
IdentifyZeros identify_zeros = kDistinguishZeros) { IdentifyZeros identify_zeros = kDistinguishZeros) {
switch (hint) { switch (hint) {
case NumberOperationHint::kSignedSmall: case NumberOperationHint::kSignedSmall:
case NumberOperationHint::kSignedSmallInputs:
return UseInfo::CheckedSignedSmallAsWord32(identify_zeros); return UseInfo::CheckedSignedSmallAsWord32(identify_zeros);
case NumberOperationHint::kSigned32: case NumberOperationHint::kSigned32:
return UseInfo::CheckedSigned32AsWord32(identify_zeros); return UseInfo::CheckedSigned32AsWord32(identify_zeros);
@ -105,6 +106,7 @@ UseInfo CheckedUseInfoAsWord32FromHint(
UseInfo CheckedUseInfoAsFloat64FromHint(NumberOperationHint hint) { UseInfo CheckedUseInfoAsFloat64FromHint(NumberOperationHint hint) {
switch (hint) { switch (hint) {
case NumberOperationHint::kSignedSmall: case NumberOperationHint::kSignedSmall:
case NumberOperationHint::kSignedSmallInputs:
case NumberOperationHint::kSigned32: case NumberOperationHint::kSigned32:
// Not used currently. // Not used currently.
UNREACHABLE(); UNREACHABLE();
@ -1654,8 +1656,8 @@ class RepresentationSelector {
// Try to use type feedback. // Try to use type feedback.
NumberOperationHint hint = NumberOperationHintOf(node->op()); NumberOperationHint hint = NumberOperationHintOf(node->op());
switch (hint) { switch (hint) {
case NumberOperationHint::kSigned32:
case NumberOperationHint::kSignedSmall: case NumberOperationHint::kSignedSmall:
case NumberOperationHint::kSigned32: {
if (propagate()) { if (propagate()) {
VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint), VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
MachineRepresentation::kBit); MachineRepresentation::kBit);
@ -1679,7 +1681,9 @@ class RepresentationSelector {
} }
} }
return; return;
} case NumberOperationHint::kSignedSmallInputs:
// This doesn't make sense for compare operations.
UNREACHABLE();
case NumberOperationHint::kNumberOrOddball: case NumberOperationHint::kNumberOrOddball:
// Abstract and strict equality don't perform ToNumber conversions // Abstract and strict equality don't perform ToNumber conversions
// on Oddballs, so make sure we don't accidentially sneak in a // on Oddballs, so make sure we don't accidentially sneak in a
@ -1850,20 +1854,22 @@ class RepresentationSelector {
} }
} }
if (hint == NumberOperationHint::kSignedSmall || if (hint == NumberOperationHint::kSigned32 ||
hint == NumberOperationHint::kSigned32) { hint == NumberOperationHint::kSignedSmall ||
hint == NumberOperationHint::kSignedSmallInputs) {
// If the result is truncated, we only need to check the inputs. // If the result is truncated, we only need to check the inputs.
if (truncation.IsUsedAsWord32()) { if (truncation.IsUsedAsWord32()) {
VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint), VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
MachineRepresentation::kWord32); MachineRepresentation::kWord32);
if (lower()) DeferReplacement(node, lowering->Int32Div(node)); if (lower()) DeferReplacement(node, lowering->Int32Div(node));
} else { return;
} else if (hint != NumberOperationHint::kSignedSmallInputs) {
VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint), VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
MachineRepresentation::kWord32, Type::Signed32()); MachineRepresentation::kWord32, Type::Signed32());
if (lower()) ChangeToInt32OverflowOp(node); if (lower()) ChangeToInt32OverflowOp(node);
}
return; return;
} }
}
// default case => Float64Div // default case => Float64Div
VisitBinop(node, UseInfo::CheckedNumberOrOddballAsFloat64(), VisitBinop(node, UseInfo::CheckedNumberOrOddballAsFloat64(),
@ -2656,6 +2662,7 @@ class RepresentationSelector {
switch (hint) { switch (hint) {
case NumberOperationHint::kSigned32: case NumberOperationHint::kSigned32:
case NumberOperationHint::kSignedSmall: case NumberOperationHint::kSignedSmall:
case NumberOperationHint::kSignedSmallInputs:
VisitUnop(node, CheckedUseInfoAsWord32FromHint(hint), VisitUnop(node, CheckedUseInfoAsWord32FromHint(hint),
MachineRepresentation::kWord32, Type::Signed32()); MachineRepresentation::kWord32, Type::Signed32());
break; break;

View File

@ -401,6 +401,8 @@ std::ostream& operator<<(std::ostream& os, NumberOperationHint hint) {
switch (hint) { switch (hint) {
case NumberOperationHint::kSignedSmall: case NumberOperationHint::kSignedSmall:
return os << "SignedSmall"; return os << "SignedSmall";
case NumberOperationHint::kSignedSmallInputs:
return os << "SignedSmallInputs";
case NumberOperationHint::kSigned32: case NumberOperationHint::kSigned32:
return os << "Signed32"; return os << "Signed32";
case NumberOperationHint::kNumber: case NumberOperationHint::kNumber:
@ -780,6 +782,8 @@ struct SimplifiedOperatorGlobalCache final {
}; \ }; \
Name##Operator<NumberOperationHint::kSignedSmall> \ Name##Operator<NumberOperationHint::kSignedSmall> \
k##Name##SignedSmallOperator; \ k##Name##SignedSmallOperator; \
Name##Operator<NumberOperationHint::kSignedSmallInputs> \
k##Name##SignedSmallInputsOperator; \
Name##Operator<NumberOperationHint::kSigned32> k##Name##Signed32Operator; \ Name##Operator<NumberOperationHint::kSigned32> k##Name##Signed32Operator; \
Name##Operator<NumberOperationHint::kNumber> k##Name##NumberOperator; \ Name##Operator<NumberOperationHint::kNumber> k##Name##NumberOperator; \
Name##Operator<NumberOperationHint::kNumberOrOddball> \ Name##Operator<NumberOperationHint::kNumberOrOddball> \
@ -940,6 +944,8 @@ const Operator* SimplifiedOperatorBuilder::SpeculativeToNumber(
switch (hint) { switch (hint) {
case NumberOperationHint::kSignedSmall: case NumberOperationHint::kSignedSmall:
return &cache_.kSpeculativeToNumberSignedSmallOperator; return &cache_.kSpeculativeToNumberSignedSmallOperator;
case NumberOperationHint::kSignedSmallInputs:
break;
case NumberOperationHint::kSigned32: case NumberOperationHint::kSigned32:
return &cache_.kSpeculativeToNumberSigned32Operator; return &cache_.kSpeculativeToNumberSigned32Operator;
case NumberOperationHint::kNumber: case NumberOperationHint::kNumber:
@ -1067,6 +1073,8 @@ const Operator* SimplifiedOperatorBuilder::StringFromCodePoint(
switch (hint) { \ switch (hint) { \
case NumberOperationHint::kSignedSmall: \ case NumberOperationHint::kSignedSmall: \
return &cache_.k##Name##SignedSmallOperator; \ return &cache_.k##Name##SignedSmallOperator; \
case NumberOperationHint::kSignedSmallInputs: \
return &cache_.k##Name##SignedSmallInputsOperator; \
case NumberOperationHint::kSigned32: \ case NumberOperationHint::kSigned32: \
return &cache_.k##Name##Signed32Operator; \ return &cache_.k##Name##Signed32Operator; \
case NumberOperationHint::kNumber: \ case NumberOperationHint::kNumber: \

View File

@ -234,8 +234,9 @@ Handle<Map> FastMapParameterOf(const Operator* op);
// A hint for speculative number operations. // A hint for speculative number operations.
enum class NumberOperationHint : uint8_t { enum class NumberOperationHint : uint8_t {
kSignedSmall, // Inputs were always Smi so far, output was in Smi range. kSignedSmall, // Inputs were Smi, output was in Smi.
kSigned32, // Inputs and output were Signed32 so far. kSignedSmallInputs, // Inputs were Smi, output was Number.
kSigned32, // Inputs were Signed32, output was Number.
kNumber, // Inputs were Number, output was Number. kNumber, // Inputs were Number, output was Number.
kNumberOrOddball, // Inputs were Number or Oddball, output was Number. kNumberOrOddball, // Inputs were Number or Oddball, output was Number.
}; };

View File

@ -181,6 +181,8 @@ BinaryOperationHint BinaryOperationHintFromFeedback(int type_feedback) {
return BinaryOperationHint::kNone; return BinaryOperationHint::kNone;
case BinaryOperationFeedback::kSignedSmall: case BinaryOperationFeedback::kSignedSmall:
return BinaryOperationHint::kSignedSmall; return BinaryOperationHint::kSignedSmall;
case BinaryOperationFeedback::kSignedSmallInputs:
return BinaryOperationHint::kSignedSmallInputs;
case BinaryOperationFeedback::kNumber: case BinaryOperationFeedback::kNumber:
return BinaryOperationHint::kNumber; return BinaryOperationHint::kNumber;
case BinaryOperationFeedback::kNumberOrOddball: case BinaryOperationFeedback::kNumberOrOddball:

View File

@ -1254,7 +1254,7 @@ inline uint32_t ObjectHash(Address address) {
// Type feedback is encoded in such a way that, we can combine the feedback // Type feedback is encoded in such a way that, we can combine the feedback
// at different points by performing an 'OR' operation. Type feedback moves // at different points by performing an 'OR' operation. Type feedback moves
// to a more generic type when we combine feedback. // to a more generic type when we combine feedback.
// kSignedSmall -> kNumber -> kNumberOrOddball -> kAny // kSignedSmall -> kSignedSmallInputs -> kNumber -> kNumberOrOddball -> kAny
// kString -> kAny // kString -> kAny
// TODO(mythria): Remove kNumber type when crankshaft can handle Oddballs // TODO(mythria): Remove kNumber type when crankshaft can handle Oddballs
// similar to Numbers. We don't need kNumber feedback for Turbofan. Extra // similar to Numbers. We don't need kNumber feedback for Turbofan. Extra
@ -1266,10 +1266,11 @@ class BinaryOperationFeedback {
enum { enum {
kNone = 0x0, kNone = 0x0,
kSignedSmall = 0x1, kSignedSmall = 0x1,
kNumber = 0x3, kSignedSmallInputs = 0x3,
kNumberOrOddball = 0x7, kNumber = 0x7,
kString = 0x8, kNumberOrOddball = 0xF,
kAny = 0x1F kString = 0x10,
kAny = 0x3F
}; };
}; };

View File

@ -463,7 +463,8 @@ Node* BinaryOpAssembler::Generate_DivideWithFeedback(
BIND(&bailout); BIND(&bailout);
{ {
var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kNumber)); var_type_feedback->Bind(
SmiConstant(BinaryOperationFeedback::kSignedSmallInputs));
Node* value = Float64Div(SmiToFloat64(lhs), SmiToFloat64(rhs)); Node* value = Float64Div(SmiToFloat64(lhs), SmiToFloat64(rhs));
var_result.Bind(AllocateHeapNumberWithValue(value)); var_result.Bind(AllocateHeapNumberWithValue(value));
Goto(&end); Goto(&end);

View File

@ -13,6 +13,8 @@ std::ostream& operator<<(std::ostream& os, BinaryOperationHint hint) {
return os << "None"; return os << "None";
case BinaryOperationHint::kSignedSmall: case BinaryOperationHint::kSignedSmall:
return os << "SignedSmall"; return os << "SignedSmall";
case BinaryOperationHint::kSignedSmallInputs:
return os << "SignedSmallInputs";
case BinaryOperationHint::kSigned32: case BinaryOperationHint::kSigned32:
return os << "Signed32"; return os << "Signed32";
case BinaryOperationHint::kNumber: case BinaryOperationHint::kNumber:

View File

@ -15,6 +15,7 @@ namespace internal {
enum class BinaryOperationHint : uint8_t { enum class BinaryOperationHint : uint8_t {
kNone, kNone,
kSignedSmall, kSignedSmall,
kSignedSmallInputs,
kSigned32, kSigned32,
kNumber, kNumber,
kNumberOrOddball, kNumberOrOddball,

View File

@ -620,7 +620,7 @@ TEST(InterpreterBinaryOpTypeFeedback) {
BinaryOperationFeedback::kSignedSmall}, BinaryOperationFeedback::kSignedSmall},
{Token::Value::DIV, ast_factory.NewSmi(3), ast_factory.NewSmi(2), {Token::Value::DIV, ast_factory.NewSmi(3), ast_factory.NewSmi(2),
isolate->factory()->NewHeapNumber(3.0 / 2.0), isolate->factory()->NewHeapNumber(3.0 / 2.0),
BinaryOperationFeedback::kNumber}, BinaryOperationFeedback::kSignedSmallInputs},
{Token::Value::DIV, ast_factory.NewNumber(3.1415), ast_factory.NewSmi(3), {Token::Value::DIV, ast_factory.NewNumber(3.1415), ast_factory.NewSmi(3),
isolate->factory()->NewHeapNumber(3.1415 / 3), isolate->factory()->NewHeapNumber(3.1415 / 3),
BinaryOperationFeedback::kNumber}, BinaryOperationFeedback::kNumber},