Commit 2851866c authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add Receiver feedback for abstract/strict equality.

Collect Receiver feedback for abstract/strict equality in Ignition and
use it in TurboFan to optimize JSEqual and JSStrictEqual operations to
pointer equality instead of having to call Equal/StrictEqual builtins.

R=jarin@chromium.org
BUG=v8:5267,v8:5400

Review-Url: https://codereview.chromium.org/2639883002
Cr-Commit-Position: refs/heads/master@{#42435}
parent ca20218e
......@@ -648,6 +648,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckNumber:
result = LowerCheckNumber(node, frame_state);
break;
case IrOpcode::kCheckReceiver:
result = LowerCheckReceiver(node, frame_state);
break;
case IrOpcode::kCheckString:
result = LowerCheckString(node, frame_state);
break;
......@@ -1154,6 +1157,22 @@ Node* EffectControlLinearizer::LowerCheckNumber(Node* node, Node* frame_state) {
return value;
}
Node* EffectControlLinearizer::LowerCheckReceiver(Node* node,
Node* frame_state) {
Node* value = node->InputAt(0);
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* value_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
Node* check = __ Uint32LessThanOrEqual(
__ Uint32Constant(FIRST_JS_RECEIVER_TYPE), value_instance_type);
__ DeoptimizeUnless(DeoptimizeReason::kNotAJavaScriptObject, check,
frame_state);
return value;
}
Node* EffectControlLinearizer::LowerCheckString(Node* node, Node* frame_state) {
Node* value = node->InputAt(0);
......
......@@ -55,6 +55,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerCheckInternalizedString(Node* node, Node* frame_state);
Node* LowerCheckMaps(Node* node, Node* frame_state);
Node* LowerCheckNumber(Node* node, Node* frame_state);
Node* LowerCheckReceiver(Node* node, Node* frame_state);
Node* LowerCheckString(Node* node, Node* frame_state);
Node* LowerCheckIf(Node* node, Node* frame_state);
Node* LowerCheckedInt32Add(Node* node, Node* frame_state);
......
......@@ -595,23 +595,24 @@ struct JSOperatorGlobalCache final {
BINARY_OP_LIST(BINARY_OP)
#undef BINARY_OP
#define COMPARE_OP(Name, properties) \
template <CompareOperationHint kHint> \
struct Name##Operator final : public Operator1<CompareOperationHint> { \
Name##Operator() \
: Operator1<CompareOperationHint>( \
IrOpcode::kJS##Name, properties, "JS" #Name, 2, 1, 1, 1, 1, \
Operator::ZeroIfNoThrow(properties), kHint) {} \
}; \
Name##Operator<CompareOperationHint::kNone> k##Name##NoneOperator; \
Name##Operator<CompareOperationHint::kSignedSmall> \
k##Name##SignedSmallOperator; \
Name##Operator<CompareOperationHint::kNumber> k##Name##NumberOperator; \
Name##Operator<CompareOperationHint::kNumberOrOddball> \
k##Name##NumberOrOddballOperator; \
Name##Operator<CompareOperationHint::kString> k##Name##StringOperator; \
Name##Operator<CompareOperationHint::kInternalizedString> \
k##Name##InternalizedStringOperator; \
#define COMPARE_OP(Name, properties) \
template <CompareOperationHint kHint> \
struct Name##Operator final : public Operator1<CompareOperationHint> { \
Name##Operator() \
: Operator1<CompareOperationHint>( \
IrOpcode::kJS##Name, properties, "JS" #Name, 2, 1, 1, 1, 1, \
Operator::ZeroIfNoThrow(properties), kHint) {} \
}; \
Name##Operator<CompareOperationHint::kNone> k##Name##NoneOperator; \
Name##Operator<CompareOperationHint::kSignedSmall> \
k##Name##SignedSmallOperator; \
Name##Operator<CompareOperationHint::kNumber> k##Name##NumberOperator; \
Name##Operator<CompareOperationHint::kNumberOrOddball> \
k##Name##NumberOrOddballOperator; \
Name##Operator<CompareOperationHint::kInternalizedString> \
k##Name##InternalizedStringOperator; \
Name##Operator<CompareOperationHint::kString> k##Name##StringOperator; \
Name##Operator<CompareOperationHint::kReceiver> k##Name##ReceiverOperator; \
Name##Operator<CompareOperationHint::kAny> k##Name##AnyOperator;
COMPARE_OP_LIST(COMPARE_OP)
#undef COMPARE_OP
......@@ -667,6 +668,8 @@ BINARY_OP_LIST(BINARY_OP)
return &cache_.k##Name##InternalizedStringOperator; \
case CompareOperationHint::kString: \
return &cache_.k##Name##StringOperator; \
case CompareOperationHint::kReceiver: \
return &cache_.k##Name##ReceiverOperator; \
case CompareOperationHint::kAny: \
return &cache_.k##Name##AnyOperator; \
} \
......
......@@ -70,6 +70,7 @@ class JSBinopReduction final {
case CompareOperationHint::kAny:
case CompareOperationHint::kNone:
case CompareOperationHint::kString:
case CompareOperationHint::kReceiver:
case CompareOperationHint::kInternalizedString:
break;
}
......@@ -87,6 +88,16 @@ class JSBinopReduction final {
return false;
}
bool IsReceiverCompareOperation() {
if (lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) {
DCHECK_EQ(1, node_->op()->EffectOutputCount());
return (CompareOperationHintOf(node_->op()) ==
CompareOperationHint::kReceiver) &&
BothInputsMaybe(Type::Receiver());
}
return false;
}
// Check if a string addition will definitely result in creating a ConsString,
// i.e. if the combined length of the resulting string exceeds the ConsString
// minimum length.
......@@ -115,6 +126,29 @@ class JSBinopReduction final {
return false;
}
// Inserts a CheckReceiver for the left input.
void CheckLeftInputToReceiver() {
Node* left_input = graph()->NewNode(simplified()->CheckReceiver(), left(),
effect(), control());
node_->ReplaceInput(0, left_input);
update_effect(left_input);
}
// Checks that both inputs are Receiver, and if we don't know
// statically that one side is already a Receiver, insert a
// CheckReceiver node.
void CheckInputsToReceiver() {
if (!left_type()->Is(Type::Receiver())) {
CheckLeftInputToReceiver();
}
if (!right_type()->Is(Type::Receiver())) {
Node* right_input = graph()->NewNode(simplified()->CheckReceiver(),
right(), effect(), control());
node_->ReplaceInput(1, right_input);
update_effect(right_input);
}
}
// Checks that both inputs are InternalizedString, and if we don't know
// statically that one side is already an InternalizedString, insert a
// CheckInternalizedString node.
......@@ -924,6 +958,9 @@ Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) {
simplified()->SpeculativeNumberEqual(hint), invert, Type::Boolean());
} else if (r.BothInputsAre(Type::Number())) {
return r.ChangeToPureOperator(simplified()->NumberEqual(), invert);
} else if (r.IsReceiverCompareOperation()) {
r.CheckInputsToReceiver();
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
return NoChange();
}
......@@ -975,6 +1012,12 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
simplified()->SpeculativeNumberEqual(hint), invert, Type::Boolean());
} else if (r.BothInputsAre(Type::Number())) {
return r.ChangeToPureOperator(simplified()->NumberEqual(), invert);
} else if (r.IsReceiverCompareOperation()) {
// For strict equality, it's enough to know that one input is a Receiver,
// as a strict equality comparison with a Receiver can only yield true if
// both sides refer to the same Receiver than.
r.CheckLeftInputToReceiver();
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
return NoChange();
}
......
......@@ -308,6 +308,7 @@
V(CheckMaps) \
V(CheckNumber) \
V(CheckInternalizedString) \
V(CheckReceiver) \
V(CheckString) \
V(CheckSmi) \
V(CheckHeapObject) \
......
......@@ -24,6 +24,7 @@ Reduction RedundancyElimination::Reduce(Node* node) {
case IrOpcode::kCheckIf:
case IrOpcode::kCheckInternalizedString:
case IrOpcode::kCheckNumber:
case IrOpcode::kCheckReceiver:
case IrOpcode::kCheckSmi:
case IrOpcode::kCheckString:
case IrOpcode::kCheckTaggedHole:
......
......@@ -2306,6 +2306,17 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kCheckReceiver: {
if (InputIs(node, Type::Receiver())) {
VisitUnop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
if (lower()) DeferReplacement(node, node->InputAt(0));
} else {
VisitUnop(node, UseInfo::CheckedHeapObjectAsTaggedPointer(),
MachineRepresentation::kTaggedPointer);
}
return;
}
case IrOpcode::kCheckSmi: {
if (SmiValuesAre32Bits() && truncation.IsUsedAsWord32()) {
VisitUnop(node, UseInfo::CheckedSignedSmallAsWord32(),
......
......@@ -502,6 +502,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
V(CheckIf, 1, 0) \
V(CheckInternalizedString, 1, 1) \
V(CheckNumber, 1, 1) \
V(CheckReceiver, 1, 1) \
V(CheckSmi, 1, 1) \
V(CheckString, 1, 1) \
V(CheckTaggedHole, 1, 1) \
......
......@@ -385,6 +385,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckNumber();
const Operator* CheckSmi();
const Operator* CheckString();
const Operator* CheckReceiver();
const Operator* CheckedInt32Add();
const Operator* CheckedInt32Sub();
......
......@@ -1731,6 +1731,11 @@ Type* Typer::Visitor::TypeCheckNumber(Node* node) {
return Type::Intersect(arg, Type::Number(), zone());
}
Type* Typer::Visitor::TypeCheckReceiver(Node* node) {
Type* arg = Operand(node, 0);
return Type::Intersect(arg, Type::Receiver(), zone());
}
Type* Typer::Visitor::TypeCheckSmi(Node* node) {
Type* arg = Operand(node, 0);
return Type::Intersect(arg, Type::SignedSmall(), zone());
......
......@@ -1101,6 +1101,10 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Number());
break;
case IrOpcode::kCheckReceiver:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Receiver());
break;
case IrOpcode::kCheckSmi:
CheckValueInputIs(node, 0, Type::Any());
break;
......
......@@ -1247,8 +1247,9 @@ 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
// kInternalizedString -> kString -> kAny
// kSignedSmall -> kNumber -> kAny
// kInternalizedString -> kString -> kAny
// kReceiver -> kAny
// TODO(epertoso): consider unifying this with BinaryOperationFeedback.
class CompareOperationFeedback {
public:
......@@ -1259,7 +1260,8 @@ class CompareOperationFeedback {
kNumberOrOddball = 0x7,
kInternalizedString = 0x8,
kString = 0x18,
kAny = 0x3F
kReceiver = 0x20,
kAny = 0x7F
};
};
......
......@@ -1096,7 +1096,15 @@ void Interpreter::DoCompareOpWithFeedback(Token::Value compare_op,
__ Goto(&gather_rhs_type);
__ Bind(&lhs_is_not_string);
var_type_feedback.Bind(__ SmiConstant(CompareOperationFeedback::kAny));
if (Token::IsEqualityOp(compare_op)) {
var_type_feedback.Bind(__ SelectSmiConstant(
__ IsJSReceiverInstanceType(lhs_instance_type),
CompareOperationFeedback::kReceiver,
CompareOperationFeedback::kAny));
} else {
var_type_feedback.Bind(
__ SmiConstant(CompareOperationFeedback::kAny));
}
__ Goto(&gather_rhs_type);
}
}
......@@ -1161,8 +1169,17 @@ void Interpreter::DoCompareOpWithFeedback(Token::Value compare_op,
__ Goto(&update_feedback);
__ Bind(&rhs_is_not_string);
var_type_feedback.Bind(
__ SmiConstant(CompareOperationFeedback::kAny));
if (Token::IsEqualityOp(compare_op)) {
var_type_feedback.Bind(
__ SmiOr(var_type_feedback.value(),
__ SelectSmiConstant(
__ IsJSReceiverInstanceType(rhs_instance_type),
CompareOperationFeedback::kReceiver,
CompareOperationFeedback::kAny)));
} else {
var_type_feedback.Bind(
__ SmiConstant(CompareOperationFeedback::kAny));
}
__ Goto(&update_feedback);
}
}
......
......@@ -158,6 +158,8 @@ CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) {
return CompareOperationHint::kInternalizedString;
case CompareOperationFeedback::kString:
return CompareOperationHint::kString;
case CompareOperationFeedback::kReceiver:
return CompareOperationHint::kReceiver;
default:
return CompareOperationHint::kAny;
}
......
......@@ -40,6 +40,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) {
return os << "InternalizedString";
case CompareOperationHint::kString:
return os << "String";
case CompareOperationHint::kReceiver:
return os << "Receiver";
case CompareOperationHint::kAny:
return os << "Any";
}
......
......@@ -35,6 +35,7 @@ enum class CompareOperationHint : uint8_t {
kNumberOrOddball,
kInternalizedString,
kString,
kReceiver,
kAny
};
......
......@@ -207,6 +207,8 @@ AstType* CompareOpHintToType(CompareOperationHint hint) {
return AstType::InternalizedString();
case CompareOperationHint::kString:
return AstType::String();
case CompareOperationHint::kReceiver:
return AstType::Receiver();
case CompareOperationHint::kAny:
return AstType::Any();
}
......
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