Commit 8f00d61d authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Collect and consume (ReceiverOr)Oddball feedback for StrictEqual.

This CL introduces proper Oddball and ReceiverOrOddball states for the
CompareOperationFeedback, and updates the StrictEqual IC to collect this
feedback as well. Previously it would not collect Oddball feedback, not
even in the sense of NumberOrOddball, since that's not usable for the
SpeculativeNumberEqual.

The new feedback is handled via newly introduced CheckReceiverOrOddball
and CheckOddball operators in TurboFan, introduced by JSTypedLowering.
Just like with the Receiver feedback, it's enough to check one side and
do a ReferenceEqual afterwards, since strict equal can only yield true
if both sides refer to the same instance.

This improves the benchmark mentioned in http://crbug.com/v8/8356 from

  naive: 2950 ms.
  tenary: 2456 ms.

to around

  naive: 2996 ms.
  tenary: 2192 ms.

which corresponds to a roughly 10% improvement in the case for the
tenary pattern, which is currently used by dart2js. In real world
scenarios this will probably help even more, since TurboFan is able
to optimize across the strict equality, i.e. there's no longer a stub
call forcibly spilling all registers that are live across the call.

This new feedback will be used as a basis for the JSEqual support for
ReceiverOrOddball, which will allow dart2js switching to the shorter
a==b form, at the same peak performance.

Bug: v8:8356
Change-Id: Iafbf5d64fcc9312f9e575b54c32c631ce9b572b2
Reviewed-on: https://chromium-review.googlesource.com/c/1297309Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56925}
parent 7d1000f3
...@@ -6201,6 +6201,10 @@ TNode<BoolT> CodeStubAssembler::IsHeapNumberInstanceType( ...@@ -6201,6 +6201,10 @@ TNode<BoolT> CodeStubAssembler::IsHeapNumberInstanceType(
return InstanceTypeEqual(instance_type, HEAP_NUMBER_TYPE); return InstanceTypeEqual(instance_type, HEAP_NUMBER_TYPE);
} }
TNode<BoolT> CodeStubAssembler::IsOddball(SloppyTNode<HeapObject> object) {
return IsOddballInstanceType(LoadInstanceType(object));
}
TNode<BoolT> CodeStubAssembler::IsOddballInstanceType( TNode<BoolT> CodeStubAssembler::IsOddballInstanceType(
SloppyTNode<Int32T> instance_type) { SloppyTNode<Int32T> instance_type) {
return InstanceTypeEqual(instance_type, ODDBALL_TYPE); return InstanceTypeEqual(instance_type, ODDBALL_TYPE);
...@@ -11267,15 +11271,16 @@ void CodeStubAssembler::GenerateEqual_Same(Node* value, Label* if_equal, ...@@ -11267,15 +11271,16 @@ void CodeStubAssembler::GenerateEqual_Same(Node* value, Label* if_equal,
if (var_type_feedback != nullptr) { if (var_type_feedback != nullptr) {
Node* instance_type = LoadMapInstanceType(value_map); Node* instance_type = LoadMapInstanceType(value_map);
Label if_string(this), if_receiver(this), if_symbol(this), if_bigint(this), Label if_string(this), if_receiver(this), if_oddball(this), if_symbol(this),
if_other(this, Label::kDeferred); if_bigint(this);
GotoIf(IsStringInstanceType(instance_type), &if_string); GotoIf(IsStringInstanceType(instance_type), &if_string);
GotoIf(IsJSReceiverInstanceType(instance_type), &if_receiver); GotoIf(IsJSReceiverInstanceType(instance_type), &if_receiver);
GotoIf(IsBigIntInstanceType(instance_type), &if_bigint); GotoIf(IsOddballInstanceType(instance_type), &if_oddball);
Branch(IsSymbolInstanceType(instance_type), &if_symbol, &if_other); Branch(IsBigIntInstanceType(instance_type), &if_bigint, &if_symbol);
BIND(&if_string); BIND(&if_string);
{ {
CSA_ASSERT(this, IsString(value));
CombineFeedback(var_type_feedback, CombineFeedback(var_type_feedback,
CollectFeedbackForString(instance_type)); CollectFeedbackForString(instance_type));
Goto(if_equal); Goto(if_equal);
...@@ -11283,25 +11288,29 @@ void CodeStubAssembler::GenerateEqual_Same(Node* value, Label* if_equal, ...@@ -11283,25 +11288,29 @@ void CodeStubAssembler::GenerateEqual_Same(Node* value, Label* if_equal,
BIND(&if_symbol); BIND(&if_symbol);
{ {
CSA_ASSERT(this, IsSymbol(value));
CombineFeedback(var_type_feedback, CompareOperationFeedback::kSymbol); CombineFeedback(var_type_feedback, CompareOperationFeedback::kSymbol);
Goto(if_equal); Goto(if_equal);
} }
BIND(&if_receiver); BIND(&if_receiver);
{ {
CSA_ASSERT(this, IsJSReceiver(value));
CombineFeedback(var_type_feedback, CompareOperationFeedback::kReceiver); CombineFeedback(var_type_feedback, CompareOperationFeedback::kReceiver);
Goto(if_equal); Goto(if_equal);
} }
BIND(&if_bigint); BIND(&if_bigint);
{ {
CSA_ASSERT(this, IsBigInt(value));
CombineFeedback(var_type_feedback, CompareOperationFeedback::kBigInt); CombineFeedback(var_type_feedback, CompareOperationFeedback::kBigInt);
Goto(if_equal); Goto(if_equal);
} }
BIND(&if_other); BIND(&if_oddball);
{ {
CombineFeedback(var_type_feedback, CompareOperationFeedback::kAny); CSA_ASSERT(this, IsOddball(value));
CombineFeedback(var_type_feedback, CompareOperationFeedback::kOddball);
Goto(if_equal); Goto(if_equal);
} }
} else { } else {
...@@ -11891,18 +11900,37 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs, ...@@ -11891,18 +11900,37 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs,
// Load the instance type of {rhs}. // Load the instance type of {rhs}.
Node* rhs_instance_type = LoadInstanceType(rhs); Node* rhs_instance_type = LoadInstanceType(rhs);
Label if_lhsissymbol(this), if_lhsisreceiver(this); Label if_lhsissymbol(this), if_lhsisreceiver(this),
if_lhsisoddball(this);
GotoIf(IsJSReceiverInstanceType(lhs_instance_type), GotoIf(IsJSReceiverInstanceType(lhs_instance_type),
&if_lhsisreceiver); &if_lhsisreceiver);
GotoIf(IsOddballInstanceType(lhs_instance_type), &if_lhsisoddball);
Branch(IsSymbolInstanceType(lhs_instance_type), &if_lhsissymbol, Branch(IsSymbolInstanceType(lhs_instance_type), &if_lhsissymbol,
&if_notequal); &if_notequal);
BIND(&if_lhsisreceiver); BIND(&if_lhsisreceiver);
{ {
GotoIfNot(IsJSReceiverInstanceType(rhs_instance_type),
&if_notequal);
var_type_feedback->Bind( var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kReceiver)); SmiConstant(CompareOperationFeedback::kReceiver));
GotoIf(IsJSReceiverInstanceType(rhs_instance_type), &if_notequal);
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kReceiverOrOddball));
GotoIf(IsOddballInstanceType(rhs_instance_type), &if_notequal);
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny));
Goto(&if_notequal);
}
BIND(&if_lhsisoddball);
{
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kOddball));
GotoIf(IsOddballInstanceType(rhs_instance_type), &if_notequal);
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kReceiverOrOddball));
GotoIf(IsJSReceiverInstanceType(rhs_instance_type), &if_notequal);
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny));
Goto(&if_notequal); Goto(&if_notequal);
} }
......
...@@ -1975,6 +1975,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1975,6 +1975,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<BoolT> IsEphemeronHashTable(SloppyTNode<HeapObject> object); TNode<BoolT> IsEphemeronHashTable(SloppyTNode<HeapObject> object);
TNode<BoolT> IsHeapNumber(SloppyTNode<HeapObject> object); TNode<BoolT> IsHeapNumber(SloppyTNode<HeapObject> object);
TNode<BoolT> IsHeapNumberInstanceType(SloppyTNode<Int32T> instance_type); TNode<BoolT> IsHeapNumberInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsOddball(SloppyTNode<HeapObject> object);
TNode<BoolT> IsOddballInstanceType(SloppyTNode<Int32T> instance_type); TNode<BoolT> IsOddballInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsIndirectStringInstanceType(SloppyTNode<Int32T> instance_type); TNode<BoolT> IsIndirectStringInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsJSArrayBuffer(SloppyTNode<HeapObject> object); TNode<BoolT> IsJSArrayBuffer(SloppyTNode<HeapObject> object);
......
...@@ -691,9 +691,15 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -691,9 +691,15 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckNumber: case IrOpcode::kCheckNumber:
result = LowerCheckNumber(node, frame_state); result = LowerCheckNumber(node, frame_state);
break; break;
case IrOpcode::kCheckOddball:
result = LowerCheckOddball(node, frame_state);
break;
case IrOpcode::kCheckReceiver: case IrOpcode::kCheckReceiver:
result = LowerCheckReceiver(node, frame_state); result = LowerCheckReceiver(node, frame_state);
break; break;
case IrOpcode::kCheckReceiverOrOddball:
result = LowerCheckReceiverOrOddball(node, frame_state);
break;
case IrOpcode::kCheckSymbol: case IrOpcode::kCheckSymbol:
result = LowerCheckSymbol(node, frame_state); result = LowerCheckSymbol(node, frame_state);
break; break;
...@@ -1588,6 +1594,21 @@ Node* EffectControlLinearizer::LowerCheckNumber(Node* node, Node* frame_state) { ...@@ -1588,6 +1594,21 @@ Node* EffectControlLinearizer::LowerCheckNumber(Node* node, Node* frame_state) {
return value; return value;
} }
Node* EffectControlLinearizer::LowerCheckOddball(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);
Node* check =
__ Word32Equal(value_instance_type, __ Uint32Constant(ODDBALL_TYPE));
__ DeoptimizeIfNot(DeoptimizeReason::kNotAnOddball, VectorSlotPair(), check,
frame_state);
return value;
}
Node* EffectControlLinearizer::LowerCheckReceiver(Node* node, Node* EffectControlLinearizer::LowerCheckReceiver(Node* node,
Node* frame_state) { Node* frame_state) {
Node* value = node->InputAt(0); Node* value = node->InputAt(0);
...@@ -1604,6 +1625,23 @@ Node* EffectControlLinearizer::LowerCheckReceiver(Node* node, ...@@ -1604,6 +1625,23 @@ Node* EffectControlLinearizer::LowerCheckReceiver(Node* node,
return value; return value;
} }
Node* EffectControlLinearizer::LowerCheckReceiverOrOddball(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_PRIMITIVE_TYPE == ODDBALL_TYPE);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
Node* check = __ Uint32LessThanOrEqual(__ Uint32Constant(ODDBALL_TYPE),
value_instance_type);
__ DeoptimizeIfNot(DeoptimizeReason::kNotAJavaScriptObjectOrOddball,
VectorSlotPair(), check, frame_state);
return value;
}
Node* EffectControlLinearizer::LowerCheckSymbol(Node* node, Node* frame_state) { Node* EffectControlLinearizer::LowerCheckSymbol(Node* node, Node* frame_state) {
Node* value = node->InputAt(0); Node* value = node->InputAt(0);
......
...@@ -67,7 +67,9 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -67,7 +67,9 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
void LowerCheckMaps(Node* node, Node* frame_state); void LowerCheckMaps(Node* node, Node* frame_state);
Node* LowerCompareMaps(Node* node); Node* LowerCompareMaps(Node* node);
Node* LowerCheckNumber(Node* node, Node* frame_state); Node* LowerCheckNumber(Node* node, Node* frame_state);
Node* LowerCheckOddball(Node* node, Node* frame_state);
Node* LowerCheckReceiver(Node* node, Node* frame_state); Node* LowerCheckReceiver(Node* node, Node* frame_state);
Node* LowerCheckReceiverOrOddball(Node* node, Node* frame_state);
Node* LowerCheckString(Node* node, Node* frame_state); Node* LowerCheckString(Node* node, Node* frame_state);
Node* LowerCheckSymbol(Node* node, Node* frame_state); Node* LowerCheckSymbol(Node* node, Node* frame_state);
void LowerCheckIf(Node* node, Node* frame_state); void LowerCheckIf(Node* node, Node* frame_state);
......
...@@ -706,6 +706,7 @@ struct JSOperatorGlobalCache final { ...@@ -706,6 +706,7 @@ struct JSOperatorGlobalCache final {
Name##Operator<CompareOperationHint::kSignedSmall> \ Name##Operator<CompareOperationHint::kSignedSmall> \
k##Name##SignedSmallOperator; \ k##Name##SignedSmallOperator; \
Name##Operator<CompareOperationHint::kNumber> k##Name##NumberOperator; \ Name##Operator<CompareOperationHint::kNumber> k##Name##NumberOperator; \
Name##Operator<CompareOperationHint::kOddball> k##Name##OddballOperator; \
Name##Operator<CompareOperationHint::kNumberOrOddball> \ Name##Operator<CompareOperationHint::kNumberOrOddball> \
k##Name##NumberOrOddballOperator; \ k##Name##NumberOrOddballOperator; \
Name##Operator<CompareOperationHint::kInternalizedString> \ Name##Operator<CompareOperationHint::kInternalizedString> \
...@@ -714,6 +715,8 @@ struct JSOperatorGlobalCache final { ...@@ -714,6 +715,8 @@ struct JSOperatorGlobalCache final {
Name##Operator<CompareOperationHint::kSymbol> k##Name##SymbolOperator; \ Name##Operator<CompareOperationHint::kSymbol> k##Name##SymbolOperator; \
Name##Operator<CompareOperationHint::kBigInt> k##Name##BigIntOperator; \ Name##Operator<CompareOperationHint::kBigInt> k##Name##BigIntOperator; \
Name##Operator<CompareOperationHint::kReceiver> k##Name##ReceiverOperator; \ Name##Operator<CompareOperationHint::kReceiver> k##Name##ReceiverOperator; \
Name##Operator<CompareOperationHint::kReceiverOrOddball> \
k##Name##ReceiverOrOddballOperator; \
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
...@@ -769,6 +772,8 @@ BINARY_OP_LIST(BINARY_OP) ...@@ -769,6 +772,8 @@ BINARY_OP_LIST(BINARY_OP)
return &cache_.k##Name##SignedSmallOperator; \ return &cache_.k##Name##SignedSmallOperator; \
case CompareOperationHint::kNumber: \ case CompareOperationHint::kNumber: \
return &cache_.k##Name##NumberOperator; \ return &cache_.k##Name##NumberOperator; \
case CompareOperationHint::kOddball: \
return &cache_.k##Name##OddballOperator; \
case CompareOperationHint::kNumberOrOddball: \ case CompareOperationHint::kNumberOrOddball: \
return &cache_.k##Name##NumberOrOddballOperator; \ return &cache_.k##Name##NumberOrOddballOperator; \
case CompareOperationHint::kInternalizedString: \ case CompareOperationHint::kInternalizedString: \
...@@ -781,6 +786,8 @@ BINARY_OP_LIST(BINARY_OP) ...@@ -781,6 +786,8 @@ BINARY_OP_LIST(BINARY_OP)
return &cache_.k##Name##BigIntOperator; \ return &cache_.k##Name##BigIntOperator; \
case CompareOperationHint::kReceiver: \ case CompareOperationHint::kReceiver: \
return &cache_.k##Name##ReceiverOperator; \ return &cache_.k##Name##ReceiverOperator; \
case CompareOperationHint::kReceiverOrOddball: \
return &cache_.k##Name##ReceiverOrOddballOperator; \
case CompareOperationHint::kAny: \ case CompareOperationHint::kAny: \
return &cache_.k##Name##AnyOperator; \ return &cache_.k##Name##AnyOperator; \
} \ } \
......
...@@ -90,7 +90,9 @@ class JSSpeculativeBinopBuilder final { ...@@ -90,7 +90,9 @@ class JSSpeculativeBinopBuilder final {
case CompareOperationHint::kString: case CompareOperationHint::kString:
case CompareOperationHint::kSymbol: case CompareOperationHint::kSymbol:
case CompareOperationHint::kBigInt: case CompareOperationHint::kBigInt:
case CompareOperationHint::kOddball:
case CompareOperationHint::kReceiver: case CompareOperationHint::kReceiver:
case CompareOperationHint::kReceiverOrOddball:
case CompareOperationHint::kInternalizedString: case CompareOperationHint::kInternalizedString:
break; break;
} }
......
...@@ -43,14 +43,21 @@ class JSBinopReduction final { ...@@ -43,14 +43,21 @@ class JSBinopReduction final {
*hint = NumberOperationHint::kNumber; *hint = NumberOperationHint::kNumber;
return true; return true;
case CompareOperationHint::kNumberOrOddball: case CompareOperationHint::kNumberOrOddball:
*hint = NumberOperationHint::kNumberOrOddball; if (node_->opcode() != IrOpcode::kJSStrictEqual) {
return true; // Number equality comparisons don't truncate Oddball
// to Number, so we must not consume this feedback.
*hint = NumberOperationHint::kNumberOrOddball;
return true;
}
V8_FALLTHROUGH;
case CompareOperationHint::kAny: case CompareOperationHint::kAny:
case CompareOperationHint::kNone: case CompareOperationHint::kNone:
case CompareOperationHint::kString: case CompareOperationHint::kString:
case CompareOperationHint::kSymbol: case CompareOperationHint::kSymbol:
case CompareOperationHint::kBigInt: case CompareOperationHint::kBigInt:
case CompareOperationHint::kOddball:
case CompareOperationHint::kReceiver: case CompareOperationHint::kReceiver:
case CompareOperationHint::kReceiverOrOddball:
case CompareOperationHint::kInternalizedString: case CompareOperationHint::kInternalizedString:
break; break;
} }
...@@ -64,6 +71,13 @@ class JSBinopReduction final { ...@@ -64,6 +71,13 @@ class JSBinopReduction final {
BothInputsMaybe(Type::InternalizedString()); BothInputsMaybe(Type::InternalizedString());
} }
bool IsOddballCompareOperation() {
DCHECK_EQ(1, node_->op()->EffectOutputCount());
return (CompareOperationHintOf(node_->op()) ==
CompareOperationHint::kOddball) &&
BothInputsMaybe(Type::Oddball());
}
bool IsReceiverCompareOperation() { bool IsReceiverCompareOperation() {
DCHECK_EQ(1, node_->op()->EffectOutputCount()); DCHECK_EQ(1, node_->op()->EffectOutputCount());
return (CompareOperationHintOf(node_->op()) == return (CompareOperationHintOf(node_->op()) ==
...@@ -71,6 +85,13 @@ class JSBinopReduction final { ...@@ -71,6 +85,13 @@ class JSBinopReduction final {
BothInputsMaybe(Type::Receiver()); BothInputsMaybe(Type::Receiver());
} }
bool IsReceiverOrOddballCompareOperation() {
DCHECK_EQ(1, node_->op()->EffectOutputCount());
return (CompareOperationHintOf(node_->op()) ==
CompareOperationHint::kReceiverOrOddball) &&
BothInputsMaybe(Type::ReceiverOrOddball());
}
bool IsStringCompareOperation() { bool IsStringCompareOperation() {
DCHECK_EQ(1, node_->op()->EffectOutputCount()); DCHECK_EQ(1, node_->op()->EffectOutputCount());
return (CompareOperationHintOf(node_->op()) == return (CompareOperationHintOf(node_->op()) ==
...@@ -114,6 +135,14 @@ class JSBinopReduction final { ...@@ -114,6 +135,14 @@ class JSBinopReduction final {
return false; return false;
} }
// Inserts a CheckOddball for the left input.
void CheckLeftInputToOddball() {
Node* left_input = graph()->NewNode(simplified()->CheckOddball(), left(),
effect(), control());
node_->ReplaceInput(0, left_input);
update_effect(left_input);
}
// Inserts a CheckReceiver for the left input. // Inserts a CheckReceiver for the left input.
void CheckLeftInputToReceiver() { void CheckLeftInputToReceiver() {
Node* left_input = graph()->NewNode(simplified()->CheckReceiver(), left(), Node* left_input = graph()->NewNode(simplified()->CheckReceiver(), left(),
...@@ -122,6 +151,14 @@ class JSBinopReduction final { ...@@ -122,6 +151,14 @@ class JSBinopReduction final {
update_effect(left_input); update_effect(left_input);
} }
// Inserts a CheckReceiverOrOddball for the left input.
void CheckLeftInputToReceiverOrOddball() {
Node* left_input = graph()->NewNode(simplified()->CheckReceiverOrOddball(),
left(), effect(), control());
node_->ReplaceInput(0, left_input);
update_effect(left_input);
}
// Checks that both inputs are Receiver, and if we don't know // Checks that both inputs are Receiver, and if we don't know
// statically that one side is already a Receiver, insert a // statically that one side is already a Receiver, insert a
// CheckReceiver node. // CheckReceiver node.
...@@ -875,12 +912,24 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node) { ...@@ -875,12 +912,24 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node) {
simplified()->SpeculativeNumberEqual(hint), Type::Boolean()); simplified()->SpeculativeNumberEqual(hint), Type::Boolean());
} else if (r.BothInputsAre(Type::Number())) { } else if (r.BothInputsAre(Type::Number())) {
return r.ChangeToPureOperator(simplified()->NumberEqual()); return r.ChangeToPureOperator(simplified()->NumberEqual());
} else if (r.IsOddballCompareOperation()) {
// For strict equality, it's enough to know that one input is an Oddball,
// as a strict equality comparison with an Oddball can only yield true if
// both sides refer to the same Oddball.
r.CheckLeftInputToOddball();
return r.ChangeToPureOperator(simplified()->ReferenceEqual());
} else if (r.IsReceiverCompareOperation()) { } else if (r.IsReceiverCompareOperation()) {
// For strict equality, it's enough to know that one input is a Receiver, // 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 // as a strict equality comparison with a Receiver can only yield true if
// both sides refer to the same Receiver. // both sides refer to the same Receiver.
r.CheckLeftInputToReceiver(); r.CheckLeftInputToReceiver();
return r.ChangeToPureOperator(simplified()->ReferenceEqual()); return r.ChangeToPureOperator(simplified()->ReferenceEqual());
} else if (r.IsReceiverOrOddballCompareOperation()) {
// For strict equality, it's enough to know that one input is a Receiver
// or an Oddball, as a strict equality comparison with a Receiver and/or
// Oddball can only yield true if both sides refer to the same instance.
r.CheckLeftInputToReceiverOrOddball();
return r.ChangeToPureOperator(simplified()->ReferenceEqual());
} else if (r.IsStringCompareOperation()) { } else if (r.IsStringCompareOperation()) {
r.CheckInputsToString(); r.CheckInputsToString();
return r.ChangeToPureOperator(simplified()->StringEqual()); return r.ChangeToPureOperator(simplified()->StringEqual());
......
...@@ -379,7 +379,9 @@ ...@@ -379,7 +379,9 @@
V(CheckMaps) \ V(CheckMaps) \
V(CheckNumber) \ V(CheckNumber) \
V(CheckInternalizedString) \ V(CheckInternalizedString) \
V(CheckOddball) \
V(CheckReceiver) \ V(CheckReceiver) \
V(CheckReceiverOrOddball) \
V(CheckString) \ V(CheckString) \
V(CheckSymbol) \ V(CheckSymbol) \
V(CheckSmi) \ V(CheckSmi) \
......
...@@ -28,7 +28,9 @@ Reduction RedundancyElimination::Reduce(Node* node) { ...@@ -28,7 +28,9 @@ Reduction RedundancyElimination::Reduce(Node* node) {
case IrOpcode::kCheckInternalizedString: case IrOpcode::kCheckInternalizedString:
case IrOpcode::kCheckNotTaggedHole: case IrOpcode::kCheckNotTaggedHole:
case IrOpcode::kCheckNumber: case IrOpcode::kCheckNumber:
case IrOpcode::kCheckOddball:
case IrOpcode::kCheckReceiver: case IrOpcode::kCheckReceiver:
case IrOpcode::kCheckReceiverOrOddball:
case IrOpcode::kCheckSmi: case IrOpcode::kCheckSmi:
case IrOpcode::kCheckString: case IrOpcode::kCheckString:
case IrOpcode::kCheckSymbol: case IrOpcode::kCheckSymbol:
...@@ -135,6 +137,12 @@ bool CheckSubsumes(Node const* a, Node const* b) { ...@@ -135,6 +137,12 @@ bool CheckSubsumes(Node const* a, Node const* b) {
} else if (a->opcode() == IrOpcode::kCheckedTaggedSignedToInt32 && } else if (a->opcode() == IrOpcode::kCheckedTaggedSignedToInt32 &&
b->opcode() == IrOpcode::kCheckedTaggedToInt32) { b->opcode() == IrOpcode::kCheckedTaggedToInt32) {
// CheckedTaggedSignedToInt32(node) implies CheckedTaggedToInt32(node) // CheckedTaggedSignedToInt32(node) implies CheckedTaggedToInt32(node)
} else if (a->opcode() == IrOpcode::kCheckOddball &&
b->opcode() == IrOpcode::kCheckReceiverOrOddball) {
// CheckOddball(node) implies CheckReceiverOrOddball(node)
} else if (a->opcode() == IrOpcode::kCheckReceiver &&
b->opcode() == IrOpcode::kCheckReceiverOrOddball) {
// CheckReceiver(node) implies CheckReceiverOrOddball(node)
} else if (a->opcode() != b->opcode()) { } else if (a->opcode() != b->opcode()) {
return false; return false;
} else { } else {
......
...@@ -1034,7 +1034,6 @@ class RepresentationSelector { ...@@ -1034,7 +1034,6 @@ class RepresentationSelector {
VisitUnop(node, UseInfo::CheckedHeapObjectAsTaggedPointer(), VisitUnop(node, UseInfo::CheckedHeapObjectAsTaggedPointer(),
MachineRepresentation::kTaggedPointer); MachineRepresentation::kTaggedPointer);
} }
return;
} }
void VisitCall(Node* node, SimplifiedLowering* lowering) { void VisitCall(Node* node, SimplifiedLowering* lowering) {
...@@ -2600,10 +2599,18 @@ class RepresentationSelector { ...@@ -2600,10 +2599,18 @@ class RepresentationSelector {
} }
return; return;
} }
case IrOpcode::kCheckOddball: {
VisitCheck(node, Type::Oddball(), lowering);
return;
}
case IrOpcode::kCheckReceiver: { case IrOpcode::kCheckReceiver: {
VisitCheck(node, Type::Receiver(), lowering); VisitCheck(node, Type::Receiver(), lowering);
return; return;
} }
case IrOpcode::kCheckReceiverOrOddball: {
VisitCheck(node, Type::ReceiverOrOddball(), lowering);
return;
}
case IrOpcode::kCheckSmi: { case IrOpcode::kCheckSmi: {
const CheckParameters& params = CheckParametersOf(node->op()); const CheckParameters& params = CheckParametersOf(node->op());
if (SmiValuesAre32Bits() && truncation.IsUsedAsWord32()) { if (SmiValuesAre32Bits() && truncation.IsUsedAsWord32()) {
......
...@@ -788,7 +788,9 @@ bool operator==(CheckMinusZeroParameters const& lhs, ...@@ -788,7 +788,9 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(CheckHeapObject, 1, 1) \ V(CheckHeapObject, 1, 1) \
V(CheckInternalizedString, 1, 1) \ V(CheckInternalizedString, 1, 1) \
V(CheckNotTaggedHole, 1, 1) \ V(CheckNotTaggedHole, 1, 1) \
V(CheckOddball, 1, 1) \
V(CheckReceiver, 1, 1) \ V(CheckReceiver, 1, 1) \
V(CheckReceiverOrOddball, 1, 1) \
V(CheckSymbol, 1, 1) \ V(CheckSymbol, 1, 1) \
V(CheckedInt32Add, 2, 1) \ V(CheckedInt32Add, 2, 1) \
V(CheckedInt32Div, 2, 1) \ V(CheckedInt32Div, 2, 1) \
......
...@@ -678,7 +678,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -678,7 +678,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const VectorSlotPair& = VectorSlotPair()); const VectorSlotPair& = VectorSlotPair());
const Operator* CheckNotTaggedHole(); const Operator* CheckNotTaggedHole();
const Operator* CheckNumber(const VectorSlotPair& feedback); const Operator* CheckNumber(const VectorSlotPair& feedback);
const Operator* CheckOddball();
const Operator* CheckReceiver(); const Operator* CheckReceiver();
const Operator* CheckReceiverOrOddball();
const Operator* CheckSmi(const VectorSlotPair& feedback); const Operator* CheckSmi(const VectorSlotPair& feedback);
const Operator* CheckString(const VectorSlotPair& feedback); const Operator* CheckString(const VectorSlotPair& feedback);
const Operator* CheckSymbol(); const Operator* CheckSymbol();
......
...@@ -2003,11 +2003,21 @@ Type Typer::Visitor::TypeCheckNumber(Node* node) { ...@@ -2003,11 +2003,21 @@ Type Typer::Visitor::TypeCheckNumber(Node* node) {
return typer_->operation_typer_.CheckNumber(Operand(node, 0)); return typer_->operation_typer_.CheckNumber(Operand(node, 0));
} }
Type Typer::Visitor::TypeCheckOddball(Node* node) {
Type arg = Operand(node, 0);
return Type::Intersect(arg, Type::Oddball(), zone());
}
Type Typer::Visitor::TypeCheckReceiver(Node* node) { Type Typer::Visitor::TypeCheckReceiver(Node* node) {
Type arg = Operand(node, 0); Type arg = Operand(node, 0);
return Type::Intersect(arg, Type::Receiver(), zone()); return Type::Intersect(arg, Type::Receiver(), zone());
} }
Type Typer::Visitor::TypeCheckReceiverOrOddball(Node* node) {
Type arg = Operand(node, 0);
return Type::Intersect(arg, Type::ReceiverOrOddball(), zone());
}
Type Typer::Visitor::TypeCheckSmi(Node* node) { Type Typer::Visitor::TypeCheckSmi(Node* node) {
Type arg = Operand(node, 0); Type arg = Operand(node, 0);
return Type::Intersect(arg, Type::SignedSmall(), zone()); return Type::Intersect(arg, Type::SignedSmall(), zone());
......
...@@ -188,6 +188,7 @@ namespace compiler { ...@@ -188,6 +188,7 @@ namespace compiler {
V(Receiver, kObject | kProxy) \ V(Receiver, kObject | kProxy) \
V(ReceiverOrUndefined, kReceiver | kUndefined) \ V(ReceiverOrUndefined, kReceiver | kUndefined) \
V(ReceiverOrNullOrUndefined, kReceiver | kNull | kUndefined) \ V(ReceiverOrNullOrUndefined, kReceiver | kNull | kUndefined) \
V(ReceiverOrOddball, kReceiver | kOddball) \
V(SymbolOrReceiver, kSymbol | kReceiver) \ V(SymbolOrReceiver, kSymbol | kReceiver) \
V(StringOrReceiver, kString | kReceiver) \ V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | \ V(Unique, kBoolean | kUniqueName | kNull | \
......
...@@ -1450,10 +1450,18 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -1450,10 +1450,18 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Number()); CheckTypeIs(node, Type::Number());
break; break;
case IrOpcode::kCheckOddball:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Oddball());
break;
case IrOpcode::kCheckReceiver: case IrOpcode::kCheckReceiver:
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Receiver()); CheckTypeIs(node, Type::Receiver());
break; break;
case IrOpcode::kCheckReceiverOrOddball:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::ReceiverOrOddball());
break;
case IrOpcode::kCheckSmi: case IrOpcode::kCheckSmi:
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
break; break;
......
...@@ -39,7 +39,9 @@ namespace internal { ...@@ -39,7 +39,9 @@ namespace internal {
V(NoCache, "no cache") \ V(NoCache, "no cache") \
V(NotAHeapNumber, "not a heap number") \ V(NotAHeapNumber, "not a heap number") \
V(NotAJavaScriptObject, "not a JavaScript object") \ V(NotAJavaScriptObject, "not a JavaScript object") \
V(NotAJavaScriptObjectOrOddball, "not a JavaScript object or Oddball") \
V(NotANumberOrOddball, "not a Number or Oddball") \ V(NotANumberOrOddball, "not a Number or Oddball") \
V(NotAnOddball, "not an Oddball") \
V(NotASmi, "not a Smi") \ V(NotASmi, "not a Smi") \
V(NotAString, "not a String") \ V(NotAString, "not a String") \
V(NotASymbol, "not a Symbol") \ V(NotASymbol, "not a Symbol") \
......
...@@ -213,6 +213,8 @@ CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) { ...@@ -213,6 +213,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::kOddball:
return CompareOperationHint::kOddball;
case CompareOperationFeedback::kNumberOrOddball: case CompareOperationFeedback::kNumberOrOddball:
return CompareOperationHint::kNumberOrOddball; return CompareOperationHint::kNumberOrOddball;
case CompareOperationFeedback::kInternalizedString: case CompareOperationFeedback::kInternalizedString:
...@@ -225,6 +227,8 @@ CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) { ...@@ -225,6 +227,8 @@ CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) {
return CompareOperationHint::kBigInt; return CompareOperationHint::kBigInt;
case CompareOperationFeedback::kReceiver: case CompareOperationFeedback::kReceiver:
return CompareOperationHint::kReceiver; return CompareOperationHint::kReceiver;
case CompareOperationFeedback::kReceiverOrOddball:
return CompareOperationHint::kReceiverOrOddball;
default: default:
return CompareOperationHint::kAny; return CompareOperationHint::kAny;
} }
......
...@@ -1309,11 +1309,13 @@ class BinaryOperationFeedback { ...@@ -1309,11 +1309,13 @@ class BinaryOperationFeedback {
// 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 -> kNumber -> kNumberOrOddball -> kAny
// kInternalizedString -> kString -> kAny // kOddball -> kNumberOrOddball -> kAny
// kSymbol -> kAny // kOddball -> kReceiverOrOddball -> kAny
// kBigInt -> kAny // kReceiver -> kReceiverOrOddball -> kAny
// kReceiver -> kAny // kInternalizedString -> kString -> kAny
// kSymbol -> kAny
// kBigInt -> kAny
// //
// This is distinct from BinaryOperationFeedback on purpose, because the // This is distinct from BinaryOperationFeedback on purpose, because the
// feedback that matters differs greatly as well as the way it is consumed. // feedback that matters differs greatly as well as the way it is consumed.
...@@ -1323,12 +1325,14 @@ class CompareOperationFeedback { ...@@ -1323,12 +1325,14 @@ class CompareOperationFeedback {
kNone = 0x00, kNone = 0x00,
kSignedSmall = 0x01, kSignedSmall = 0x01,
kNumber = 0x3, kNumber = 0x3,
kNumberOrOddball = 0x7, kOddball = 0x04,
kNumberOrOddball = kNumber | kOddball,
kInternalizedString = 0x8, kInternalizedString = 0x8,
kString = 0x18, kString = 0x18,
kSymbol = 0x20, kSymbol = 0x20,
kBigInt = 0x30, kBigInt = 0x30,
kReceiver = 0x40, kReceiver = 0x40,
kReceiverOrOddball = kReceiver | kOddball,
kAny = 0xff kAny = 0xff
}; };
}; };
......
...@@ -39,6 +39,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) { ...@@ -39,6 +39,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) {
return os << "SignedSmall"; return os << "SignedSmall";
case CompareOperationHint::kNumber: case CompareOperationHint::kNumber:
return os << "Number"; return os << "Number";
case CompareOperationHint::kOddball:
return os << "Oddball";
case CompareOperationHint::kNumberOrOddball: case CompareOperationHint::kNumberOrOddball:
return os << "NumberOrOddball"; return os << "NumberOrOddball";
case CompareOperationHint::kInternalizedString: case CompareOperationHint::kInternalizedString:
...@@ -51,6 +53,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) { ...@@ -51,6 +53,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) {
return os << "BigInt"; return os << "BigInt";
case CompareOperationHint::kReceiver: case CompareOperationHint::kReceiver:
return os << "Receiver"; return os << "Receiver";
case CompareOperationHint::kReceiverOrOddball:
return os << "ReceiverOrOddball";
case CompareOperationHint::kAny: case CompareOperationHint::kAny:
return os << "Any"; return os << "Any";
} }
......
...@@ -35,12 +35,14 @@ enum class CompareOperationHint : uint8_t { ...@@ -35,12 +35,14 @@ enum class CompareOperationHint : uint8_t {
kNone, kNone,
kSignedSmall, kSignedSmall,
kNumber, kNumber,
kOddball,
kNumberOrOddball, kNumberOrOddball,
kInternalizedString, kInternalizedString,
kString, kString,
kSymbol, kSymbol,
kBigInt, kBigInt,
kReceiver, kReceiver,
kReceiverOrOddball,
kAny kAny
}; };
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
// Make sure that we don't incorrectly truncate Oddball
// to Number for strict equality comparisons.
(function() {
function foo(x, y) { return x === y; }
assertTrue(foo(0.1, 0.1));
assertTrue(foo(undefined, undefined));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(undefined, undefined));
})();
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --opt --noalways-opt
// Known oddballs strict equality.
(function() {
const a = null;
const b = undefined;
function foo() { return a === b; }
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
// Known oddball on one side strict equality.
(function() {
const a = true;
const b = false;
function foo(a) { return a === b; }
assertTrue(foo(b));
assertFalse(foo(a));
assertTrue(foo(b));
assertFalse(foo(a));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(b));
assertFalse(foo(a));
})();
// Feedback based oddball strict equality.
(function() {
const a = null;
const b = undefined;
function foo(a, b) { return a === b; }
assertTrue(foo(b, b));
assertFalse(foo(a, b));
assertTrue(foo(a, a));
assertFalse(foo(b, a));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(a, a));
assertFalse(foo(b, a));
// TurboFan bakes in feedback for the left hand side.
assertFalse(foo({}, b));
assertUnoptimized(foo);
})();
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --opt --noalways-opt
// Known receivers strict equality.
(function() {
const a = {};
const b = {};
function foo() { return a === b; }
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
// Known receiver/null strict equality.
(function() {
const a = {};
const b = null;
function foo() { return a === b; }
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
// Known receiver/undefined strict equality.
(function() {
const a = {};
const b = undefined;
function foo() { return a === b; }
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
// Known receiver on one side strict equality.
(function() {
const a = {};
const b = {};
function foo(a) { return a === b; }
assertTrue(foo(b));
assertFalse(foo(a));
assertTrue(foo(b));
assertFalse(foo(a));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(b));
assertFalse(foo(a));
})();
// Known receiver on one side strict equality.
(function() {
const a = {};
const b = null;
function foo(a) { return a === b; }
assertTrue(foo(b));
assertFalse(foo(a));
assertTrue(foo(b));
assertFalse(foo(a));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(b));
assertFalse(foo(a));
})();
// Known receiver on one side strict equality.
(function() {
const a = {};
const b = undefined;
function foo(a) { return a === b; }
assertTrue(foo(b));
assertFalse(foo(a));
assertTrue(foo(b));
assertFalse(foo(a));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(b));
assertFalse(foo(a));
})();
// Feedback based receiver strict equality.
(function() {
const a = {};
const b = {};
function foo(a, b) { return a === b; }
assertTrue(foo(b, b));
assertFalse(foo(a, b));
assertTrue(foo(a, a));
assertFalse(foo(b, a));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(a, a));
assertFalse(foo(b, a));
// TurboFan bakes in feedback for the left hand side.
assertFalse(foo(null, b));
assertUnoptimized(foo);
})();
// Feedback based receiver/null strict equality.
(function() {
const a = {};
const b = null;
function foo(a, b) { return a === b; }
assertTrue(foo(b, b));
assertFalse(foo(a, b));
assertTrue(foo(a, a));
assertFalse(foo(b, a));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(a, a));
assertFalse(foo(b, a));
// TurboFan bakes in feedback for the left hand side.
assertFalse(foo(1, b));
assertUnoptimized(foo);
})();
// Feedback based receiver/undefined strict equality.
(function() {
const a = {};
const b = undefined;
function foo(a, b) { return a === b; }
assertTrue(foo(b, b));
assertFalse(foo(a, b));
assertTrue(foo(a, a));
assertFalse(foo(b, a));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(a, a));
assertFalse(foo(b, a));
// TurboFan bakes in feedback for the left hand side.
assertFalse(foo(1, b));
assertUnoptimized(foo);
})();
...@@ -132,6 +132,107 @@ TEST_F(RedundancyEliminationTest, CheckNumberSubsumedByCheckSmi) { ...@@ -132,6 +132,107 @@ TEST_F(RedundancyEliminationTest, CheckNumberSubsumedByCheckSmi) {
} }
} }
// -----------------------------------------------------------------------------
// CheckOddball
TEST_F(RedundancyEliminationTest, CheckOddball) {
Node* value = Parameter(0);
Node* effect = graph()->start();
Node* control = graph()->start();
Node* check1 = effect =
graph()->NewNode(simplified()->CheckOddball(), value, effect, control);
Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect =
graph()->NewNode(simplified()->CheckOddball(), value, effect, control);
Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1);
}
// -----------------------------------------------------------------------------
// CheckReceiver
TEST_F(RedundancyEliminationTest, CheckReceiver) {
Node* value = Parameter(0);
Node* effect = graph()->start();
Node* control = graph()->start();
Node* check1 = effect =
graph()->NewNode(simplified()->CheckReceiver(), value, effect, control);
Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect =
graph()->NewNode(simplified()->CheckReceiver(), value, effect, control);
Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1);
}
// -----------------------------------------------------------------------------
// CheckReceiverOrOddball
TEST_F(RedundancyEliminationTest, CheckReceiverOrOddball) {
Node* value = Parameter(0);
Node* effect = graph()->start();
Node* control = graph()->start();
Node* check1 = effect = graph()->NewNode(
simplified()->CheckReceiverOrOddball(), value, effect, control);
Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect = graph()->NewNode(
simplified()->CheckReceiverOrOddball(), value, effect, control);
Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1);
}
TEST_F(RedundancyEliminationTest,
CheckReceiverOrOddballSubsumedByCheckOddball) {
Node* value = Parameter(0);
Node* effect = graph()->start();
Node* control = graph()->start();
Node* check1 = effect =
graph()->NewNode(simplified()->CheckOddball(), value, effect, control);
Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect = graph()->NewNode(
simplified()->CheckReceiverOrOddball(), value, effect, control);
Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1);
}
TEST_F(RedundancyEliminationTest,
CheckReceiverOrOddballSubsumedByCheckReceiver) {
Node* value = Parameter(0);
Node* effect = graph()->start();
Node* control = graph()->start();
Node* check1 = effect =
graph()->NewNode(simplified()->CheckReceiver(), value, effect, control);
Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect = graph()->NewNode(
simplified()->CheckReceiverOrOddball(), value, effect, control);
Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// CheckString // CheckString
......
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