Commit 53698740 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Collect String feedback on CompareOps.

Collect string feedback for compare operations. Without this,
functions which have a lot of string compare operations end up with
a high generic type percentage, and don't get optimized until very
late.

Currently TurboFan doesn't use this String feedback for compare
operations, but this could be done in future work if it is useful.

BUG=chromium:660947

Review-Url: https://codereview.chromium.org/2506013005
Cr-Commit-Position: refs/heads/master@{#41078}
parent 29745ee9
...@@ -526,6 +526,7 @@ struct JSOperatorGlobalCache final { ...@@ -526,6 +526,7 @@ struct JSOperatorGlobalCache final {
Name##Operator<CompareOperationHint::kNumber> k##Name##NumberOperator; \ Name##Operator<CompareOperationHint::kNumber> k##Name##NumberOperator; \
Name##Operator<CompareOperationHint::kNumberOrOddball> \ Name##Operator<CompareOperationHint::kNumberOrOddball> \
k##Name##NumberOrOddballOperator; \ k##Name##NumberOrOddballOperator; \
Name##Operator<CompareOperationHint::kString> k##Name##StringOperator; \
Name##Operator<CompareOperationHint::kAny> k##Name##AnyOperator; Name##Operator<CompareOperationHint::kAny> k##Name##AnyOperator;
COMPARE_OP_LIST(COMPARE_OP) COMPARE_OP_LIST(COMPARE_OP)
#undef COMPARE_OP #undef COMPARE_OP
...@@ -577,6 +578,8 @@ BINARY_OP_LIST(BINARY_OP) ...@@ -577,6 +578,8 @@ BINARY_OP_LIST(BINARY_OP)
return &cache_.k##Name##NumberOperator; \ return &cache_.k##Name##NumberOperator; \
case CompareOperationHint::kNumberOrOddball: \ case CompareOperationHint::kNumberOrOddball: \
return &cache_.k##Name##NumberOrOddballOperator; \ return &cache_.k##Name##NumberOrOddballOperator; \
case CompareOperationHint::kString: \
return &cache_.k##Name##StringOperator; \
case CompareOperationHint::kAny: \ case CompareOperationHint::kAny: \
return &cache_.k##Name##AnyOperator; \ return &cache_.k##Name##AnyOperator; \
} \ } \
......
...@@ -69,6 +69,7 @@ class JSBinopReduction final { ...@@ -69,6 +69,7 @@ class JSBinopReduction final {
return true; return true;
case CompareOperationHint::kAny: case CompareOperationHint::kAny:
case CompareOperationHint::kNone: case CompareOperationHint::kNone:
case CompareOperationHint::kString:
break; break;
} }
} }
......
...@@ -50,6 +50,7 @@ CompareOperationHint ToCompareOperationHint(Token::Value op, ...@@ -50,6 +50,7 @@ CompareOperationHint ToCompareOperationHint(Token::Value op,
: CompareOperationHint::kNumber; : CompareOperationHint::kNumber;
case CompareICState::STRING: case CompareICState::STRING:
case CompareICState::INTERNALIZED_STRING: case CompareICState::INTERNALIZED_STRING:
return CompareOperationHint::kString;
case CompareICState::UNIQUE_NAME: case CompareICState::UNIQUE_NAME:
case CompareICState::RECEIVER: case CompareICState::RECEIVER:
case CompareICState::KNOWN_RECEIVER: case CompareICState::KNOWN_RECEIVER:
......
...@@ -1241,10 +1241,21 @@ class BinaryOperationFeedback { ...@@ -1241,10 +1241,21 @@ class BinaryOperationFeedback {
}; };
}; };
// 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
// to a more generic type when we combine feedback.
// kSignedSmall -> kNumber -> kAny
// kString -> kAny
// TODO(epertoso): consider unifying this with BinaryOperationFeedback. // TODO(epertoso): consider unifying this with BinaryOperationFeedback.
class CompareOperationFeedback { class CompareOperationFeedback {
public: public:
enum { kNone = 0x00, kSignedSmall = 0x01, kNumber = 0x3, kAny = 0x7 }; enum {
kNone = 0x00,
kSignedSmall = 0x01,
kNumber = 0x3,
kString = 0x4,
kAny = 0xf
};
}; };
// Describes how exactly a frame has been dropped from stack. // Describes how exactly a frame has been dropped from stack.
......
...@@ -1000,62 +1000,91 @@ void Interpreter::DoCompareOpWithFeedback(Token::Value compare_op, ...@@ -1000,62 +1000,91 @@ void Interpreter::DoCompareOpWithFeedback(Token::Value compare_op,
// sometimes emit comparisons that shouldn't collect feedback (e.g. // sometimes emit comparisons that shouldn't collect feedback (e.g.
// try-finally blocks and generators), and we could get rid of this by // try-finally blocks and generators), and we could get rid of this by
// introducing Smi equality tests. // introducing Smi equality tests.
Label skip_feedback_update(assembler); Label gather_type_feedback(assembler), do_compare(assembler);
__ GotoIf(__ WordEqual(slot_index, __ IntPtrConstant(0)), __ Branch(__ WordEqual(slot_index, __ IntPtrConstant(0)), &do_compare,
&skip_feedback_update); &gather_type_feedback);
Variable var_type_feedback(assembler, MachineRepresentation::kWord32); __ Bind(&gather_type_feedback);
Label lhs_is_smi(assembler), lhs_is_not_smi(assembler),
gather_rhs_type(assembler), do_compare(assembler);
__ Branch(__ TaggedIsSmi(lhs), &lhs_is_smi, &lhs_is_not_smi);
__ Bind(&lhs_is_smi);
var_type_feedback.Bind(
__ Int32Constant(CompareOperationFeedback::kSignedSmall));
__ Goto(&gather_rhs_type);
__ Bind(&lhs_is_not_smi);
{ {
Label lhs_is_number(assembler), lhs_is_not_number(assembler); Variable var_type_feedback(assembler, MachineRepresentation::kWord32);
Node* lhs_map = __ LoadMap(lhs); Label lhs_is_not_smi(assembler), lhs_is_not_number(assembler),
__ Branch(__ WordEqual(lhs_map, __ HeapNumberMapConstant()), &lhs_is_number, lhs_is_not_string(assembler), gather_rhs_type(assembler),
&lhs_is_not_number); update_feedback(assembler);
__ GotoUnless(__ TaggedIsSmi(lhs), &lhs_is_not_smi);
__ Bind(&lhs_is_number); var_type_feedback.Bind(
var_type_feedback.Bind(__ Int32Constant(CompareOperationFeedback::kNumber)); __ Int32Constant(CompareOperationFeedback::kSignedSmall));
__ Goto(&gather_rhs_type); __ Goto(&gather_rhs_type);
__ Bind(&lhs_is_not_number); __ Bind(&lhs_is_not_smi);
var_type_feedback.Bind(__ Int32Constant(CompareOperationFeedback::kAny)); {
__ Goto(&do_compare); Node* lhs_map = __ LoadMap(lhs);
} __ GotoUnless(__ WordEqual(lhs_map, __ HeapNumberMapConstant()),
&lhs_is_not_number);
var_type_feedback.Bind(
__ Int32Constant(CompareOperationFeedback::kNumber));
__ Goto(&gather_rhs_type);
__ Bind(&lhs_is_not_number);
{
Node* lhs_instance_type = __ LoadInstanceType(lhs);
Node* lhs_type =
__ Select(__ IsStringInstanceType(lhs_instance_type),
__ Int32Constant(CompareOperationFeedback::kString),
__ Int32Constant(CompareOperationFeedback::kAny));
var_type_feedback.Bind(lhs_type);
__ Goto(&gather_rhs_type);
}
}
__ Bind(&gather_rhs_type); __ Bind(&gather_rhs_type);
{ {
Label rhs_is_smi(assembler); Label rhs_is_not_smi(assembler), rhs_is_not_number(assembler);
__ GotoIf(__ TaggedIsSmi(rhs), &rhs_is_smi);
__ GotoUnless(__ TaggedIsSmi(rhs), &rhs_is_not_smi);
Node* rhs_map = __ LoadMap(rhs);
Node* rhs_type = var_type_feedback.Bind(__ Word32Or(
__ Select(__ WordEqual(rhs_map, __ HeapNumberMapConstant()), var_type_feedback.value(),
__ Int32Constant(CompareOperationFeedback::kNumber), __ Int32Constant(CompareOperationFeedback::kSignedSmall)));
__ Int32Constant(CompareOperationFeedback::kAny)); __ Goto(&update_feedback);
var_type_feedback.Bind(__ Word32Or(var_type_feedback.value(), rhs_type));
__ Goto(&do_compare); __ Bind(&rhs_is_not_smi);
{
__ Bind(&rhs_is_smi); Node* rhs_map = __ LoadMap(rhs);
var_type_feedback.Bind( __ GotoUnless(__ WordEqual(rhs_map, __ HeapNumberMapConstant()),
__ Word32Or(var_type_feedback.value(), &rhs_is_not_number);
__ Int32Constant(CompareOperationFeedback::kSignedSmall)));
__ Goto(&do_compare); var_type_feedback.Bind(
__ Word32Or(var_type_feedback.value(),
__ Int32Constant(CompareOperationFeedback::kNumber)));
__ Goto(&update_feedback);
__ Bind(&rhs_is_not_number);
{
Node* rhs_instance_type = __ LoadInstanceType(rhs);
Node* rhs_type =
__ Select(__ IsStringInstanceType(rhs_instance_type),
__ Int32Constant(CompareOperationFeedback::kString),
__ Int32Constant(CompareOperationFeedback::kAny));
var_type_feedback.Bind(
__ Word32Or(var_type_feedback.value(), rhs_type));
__ Goto(&update_feedback);
}
}
}
__ Bind(&update_feedback);
{
__ UpdateFeedback(var_type_feedback.value(), type_feedback_vector,
slot_index);
__ Goto(&do_compare);
}
} }
__ Bind(&do_compare); __ Bind(&do_compare);
__ UpdateFeedback(var_type_feedback.value(), type_feedback_vector,
slot_index);
__ Goto(&skip_feedback_update);
__ Bind(&skip_feedback_update);
Node* result; Node* result;
switch (compare_op) { switch (compare_op) {
case Token::EQ: case Token::EQ:
......
...@@ -149,6 +149,8 @@ CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) { ...@@ -149,6 +149,8 @@ CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) {
return CompareOperationHint::kSignedSmall; return CompareOperationHint::kSignedSmall;
case CompareOperationFeedback::kNumber: case CompareOperationFeedback::kNumber:
return CompareOperationHint::kNumber; return CompareOperationHint::kNumber;
case CompareOperationFeedback::kString:
return CompareOperationHint::kString;
default: default:
return CompareOperationHint::kAny; return CompareOperationHint::kAny;
} }
......
...@@ -36,6 +36,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) { ...@@ -36,6 +36,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) {
return os << "Number"; return os << "Number";
case CompareOperationHint::kNumberOrOddball: case CompareOperationHint::kNumberOrOddball:
return os << "NumberOrOddball"; return os << "NumberOrOddball";
case CompareOperationHint::kString:
return os << "String";
case CompareOperationHint::kAny: case CompareOperationHint::kAny:
return os << "Any"; return os << "Any";
} }
......
...@@ -33,6 +33,7 @@ enum class CompareOperationHint : uint8_t { ...@@ -33,6 +33,7 @@ enum class CompareOperationHint : uint8_t {
kSignedSmall, kSignedSmall,
kNumber, kNumber,
kNumberOrOddball, kNumberOrOddball,
kString,
kAny kAny
}; };
......
...@@ -203,6 +203,8 @@ AstType* CompareOpHintToType(CompareOperationHint hint) { ...@@ -203,6 +203,8 @@ AstType* CompareOpHintToType(CompareOperationHint hint) {
return AstType::Number(); return AstType::Number();
case CompareOperationHint::kNumberOrOddball: case CompareOperationHint::kNumberOrOddball:
return AstType::NumberOrOddball(); return AstType::NumberOrOddball();
case CompareOperationHint::kString:
return AstType::String();
case CompareOperationHint::kAny: case CompareOperationHint::kAny:
return AstType::Any(); return AstType::Any();
} }
......
...@@ -555,6 +555,10 @@ TEST(InterpreterBinaryOpTypeFeedback) { ...@@ -555,6 +555,10 @@ TEST(InterpreterBinaryOpTypeFeedback) {
isolate->factory()->NewHeapNumber(1.4142), isolate->factory()->NewHeapNumber(1.4142),
isolate->factory()->NewHeapNumber(3.1415 + 1.4142), isolate->factory()->NewHeapNumber(3.1415 + 1.4142),
BinaryOperationFeedback::kNumber}, BinaryOperationFeedback::kNumber},
{Token::Value::ADD, isolate->factory()->NewStringFromAsciiChecked("foo"),
isolate->factory()->NewStringFromAsciiChecked("bar"),
isolate->factory()->NewStringFromAsciiChecked("foobar"),
BinaryOperationFeedback::kString},
{Token::Value::ADD, Handle<Smi>(Smi::FromInt(2), isolate), {Token::Value::ADD, Handle<Smi>(Smi::FromInt(2), isolate),
isolate->factory()->NewStringFromAsciiChecked("2"), isolate->factory()->NewStringFromAsciiChecked("2"),
isolate->factory()->NewStringFromAsciiChecked("22"), isolate->factory()->NewStringFromAsciiChecked("22"),
...@@ -1809,7 +1813,7 @@ TEST(InterpreterStringComparisons) { ...@@ -1809,7 +1813,7 @@ TEST(InterpreterStringComparisons) {
CompareC(comparison, inputs[i], inputs[j])); CompareC(comparison, inputs[i], inputs[j]));
Object* feedback = vector->Get(slot); Object* feedback = vector->Get(slot);
CHECK(feedback->IsSmi()); CHECK(feedback->IsSmi());
CHECK_EQ(CompareOperationFeedback::kAny, CHECK_EQ(CompareOperationFeedback::kString,
static_cast<Smi*>(feedback)->value()); static_cast<Smi*>(feedback)->value());
} }
} }
...@@ -1822,7 +1826,7 @@ TEST(InterpreterMixedComparisons) { ...@@ -1822,7 +1826,7 @@ TEST(InterpreterMixedComparisons) {
// convertible to a HeapNumber so comparison will be between numeric // convertible to a HeapNumber so comparison will be between numeric
// values except for the strict comparisons where no conversion is // values except for the strict comparisons where no conversion is
// performed. // performed.
const char* inputs[] = {"-1.77", "-40.333", "0.01", "55.77e5", "2.01"}; const char* inputs[] = {"-1.77", "-40.333", "0.01", "55.77e50", "2.01"};
UnicodeCache unicode_cache; UnicodeCache unicode_cache;
...@@ -1875,8 +1879,10 @@ TEST(InterpreterMixedComparisons) { ...@@ -1875,8 +1879,10 @@ TEST(InterpreterMixedComparisons) {
CompareC(comparison, lhs, rhs, true)); CompareC(comparison, lhs, rhs, true));
Object* feedback = vector->Get(slot); Object* feedback = vector->Get(slot);
CHECK(feedback->IsSmi()); CHECK(feedback->IsSmi());
CHECK_EQ(CompareOperationFeedback::kAny, // kNumber | kString gets converted to CompareOperationHint::kAny.
static_cast<Smi*>(feedback)->value()); int expected_feedback = CompareOperationFeedback::kNumber |
CompareOperationFeedback::kString;
CHECK_EQ(expected_feedback, static_cast<Smi*>(feedback)->value());
} }
} }
} }
......
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