Commit f19c4a59 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] ReceiverOrNullOrUndefined feedback for JSEqual.

This changes the ReceiverOrOddball feedback on JSStrictEqual to
ReceiverOrNullOrUndefined feedback, which can also safely be
consumed by JSEqual (we cannot generally accept any oddball here
since booleans trigger implicit conversions, unfortunately).
Thus we replace the previously introduced CheckReceiverOrOddball
with CheckReceiverOrNullOrUndefined, and drop CheckOddball, since
we will no longer collect Oddball feedback separately.

TurboFan will then turn a JSEqual[ReceiverOrNullOrUndefined] into
a sequence like this:

```
left = CheckReceiverOrNullOrUndefined(left);
right = CheckReceiverOrNullOrUndefined(right);
result = if ObjectIsUndetectable(left) then
           ObjectIsUndetectable(right)
         else
           ReferenceEqual(left, right);
```

This significantly improves the peak performance of abstract equality
with Receiver, Null or Undefined inputs. On the test case outlined in
http://crbug.com/v8/8356 we go from

  naive: 2946 ms.
  tenary: 2134 ms.

to

  naive: 2230 ms.
  tenary: 2250 ms.

which corresponds to a 25% improvement on the abstract equality case.
For regular code this will probably yield more performance, since we
get rid of the JSEqual operator, which might have arbitrary side
effects and thus blocks all kinds of TurboFan optimizations. The
JSStrictEqual case is slightly slower now, since it has to rule out
booleans as well (even though that's not strictly necessary, but
consistency is key here).

This way developers can safely use `a == b` instead of doing a dance
like `a == null ? b == null : a === b` (which is what dart2js does
right now) when both `a` and `b` are known to be Receiver, Null or
Undefined. The abstract equality is not only faster to parse than
the tenary, but also generates a shorter bytecode sequence. In the
test case referenced in http://crbug.com/v8/8356 the bytecode for
`naive` is

```
StackCheck
Ldar a1
TestEqual a0, [0]
JumpIfFalse [5]
LdaSmi [1]
Return
LdaSmi [2]
Return
```

which is 14 bytes, whereas the `tenary` function generates

```
StackCheck
Ldar a0
TestUndetectable
JumpIfFalse [7]
Ldar a1
TestUndetectable
Jump [7]
Ldar a1
TestEqualStrict a0, [0]
JumpIfToBooleanFalse [5]
LdaSmi [1]
Return
LdaSmi [2]
Return
```

which is 24 bytes. So the `naive` version is 40% smaller and requires
fewer bytecode dispatches.

Bug: chromium:898455, v8:8356
Change-Id: If3961b2518b4438700706b3bd6071d546305e233
Reviewed-on: https://chromium-review.googlesource.com/c/1297315Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56948}
parent 9b939128
This diff is collapsed.
...@@ -691,14 +691,11 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -691,14 +691,11 @@ 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: case IrOpcode::kCheckReceiverOrNullOrUndefined:
result = LowerCheckReceiverOrOddball(node, frame_state); result = LowerCheckReceiverOrNullOrUndefined(node, frame_state);
break; break;
case IrOpcode::kCheckSymbol: case IrOpcode::kCheckSymbol:
result = LowerCheckSymbol(node, frame_state); result = LowerCheckSymbol(node, frame_state);
...@@ -1594,21 +1591,6 @@ Node* EffectControlLinearizer::LowerCheckNumber(Node* node, Node* frame_state) { ...@@ -1594,21 +1591,6 @@ 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);
...@@ -1625,20 +1607,26 @@ Node* EffectControlLinearizer::LowerCheckReceiver(Node* node, ...@@ -1625,20 +1607,26 @@ Node* EffectControlLinearizer::LowerCheckReceiver(Node* node,
return value; return value;
} }
Node* EffectControlLinearizer::LowerCheckReceiverOrOddball(Node* node, Node* EffectControlLinearizer::LowerCheckReceiverOrNullOrUndefined(
Node* frame_state) { Node* node, Node* frame_state) {
Node* value = node->InputAt(0); Node* value = node->InputAt(0);
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* value_instance_type = Node* value_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map); __ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
// Rule out all primitives except oddballs (true, false, undefined, null).
STATIC_ASSERT(LAST_PRIMITIVE_TYPE == ODDBALL_TYPE); STATIC_ASSERT(LAST_PRIMITIVE_TYPE == ODDBALL_TYPE);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
Node* check = __ Uint32LessThanOrEqual(__ Uint32Constant(ODDBALL_TYPE), Node* check0 = __ Uint32LessThanOrEqual(__ Uint32Constant(ODDBALL_TYPE),
value_instance_type); value_instance_type);
__ DeoptimizeIfNot(DeoptimizeReason::kNotAJavaScriptObjectOrOddball, __ DeoptimizeIfNot(DeoptimizeReason::kNotAJavaScriptObjectOrNullOrUndefined,
VectorSlotPair(), check, frame_state); VectorSlotPair(), check0, frame_state);
// Rule out booleans.
Node* check1 = __ WordEqual(value_map, __ BooleanMapConstant());
__ DeoptimizeIf(DeoptimizeReason::kNotAJavaScriptObjectOrNullOrUndefined,
VectorSlotPair(), check1, frame_state);
return value; return value;
} }
......
...@@ -67,9 +67,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -67,9 +67,8 @@ 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* LowerCheckReceiverOrNullOrUndefined(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);
......
...@@ -90,6 +90,7 @@ namespace compiler { ...@@ -90,6 +90,7 @@ namespace compiler {
V(TrueConstant) \ V(TrueConstant) \
V(FalseConstant) \ V(FalseConstant) \
V(NullConstant) \ V(NullConstant) \
V(BooleanMapConstant) \
V(HeapNumberMapConstant) \ V(HeapNumberMapConstant) \
V(NoContextConstant) \ V(NoContextConstant) \
V(EmptyStringConstant) \ V(EmptyStringConstant) \
......
...@@ -134,6 +134,8 @@ DEFINE_GETTER(AllocateInOldSpaceStubConstant, ...@@ -134,6 +134,8 @@ DEFINE_GETTER(AllocateInOldSpaceStubConstant,
DEFINE_GETTER(ArrayConstructorStubConstant, DEFINE_GETTER(ArrayConstructorStubConstant,
HeapConstant(BUILTIN_CODE(isolate(), ArrayConstructorImpl))) HeapConstant(BUILTIN_CODE(isolate(), ArrayConstructorImpl)))
DEFINE_GETTER(BooleanMapConstant, HeapConstant(factory()->boolean_map()))
DEFINE_GETTER(ToNumberBuiltinConstant, DEFINE_GETTER(ToNumberBuiltinConstant,
HeapConstant(BUILTIN_CODE(isolate(), ToNumber))) HeapConstant(BUILTIN_CODE(isolate(), ToNumber)))
......
...@@ -84,6 +84,7 @@ class V8_EXPORT_PRIVATE JSGraph : public MachineGraph { ...@@ -84,6 +84,7 @@ class V8_EXPORT_PRIVATE JSGraph : public MachineGraph {
V(AllocateInNewSpaceStubConstant) \ V(AllocateInNewSpaceStubConstant) \
V(AllocateInOldSpaceStubConstant) \ V(AllocateInOldSpaceStubConstant) \
V(ArrayConstructorStubConstant) \ V(ArrayConstructorStubConstant) \
V(BooleanMapConstant) \
V(ToNumberBuiltinConstant) \ V(ToNumberBuiltinConstant) \
V(EmptyFixedArrayConstant) \ V(EmptyFixedArrayConstant) \
V(EmptyStringConstant) \ V(EmptyStringConstant) \
......
...@@ -706,7 +706,6 @@ struct JSOperatorGlobalCache final { ...@@ -706,7 +706,6 @@ 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> \
...@@ -715,8 +714,8 @@ struct JSOperatorGlobalCache final { ...@@ -715,8 +714,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> \ Name##Operator<CompareOperationHint::kReceiverOrNullOrUndefined> \
k##Name##ReceiverOrOddballOperator; \ k##Name##ReceiverOrNullOrUndefinedOperator; \
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
...@@ -772,8 +771,6 @@ BINARY_OP_LIST(BINARY_OP) ...@@ -772,8 +771,6 @@ 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: \
...@@ -786,8 +783,8 @@ BINARY_OP_LIST(BINARY_OP) ...@@ -786,8 +783,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: \ case CompareOperationHint::kReceiverOrNullOrUndefined: \
return &cache_.k##Name##ReceiverOrOddballOperator; \ return &cache_.k##Name##ReceiverOrNullOrUndefinedOperator; \
case CompareOperationHint::kAny: \ case CompareOperationHint::kAny: \
return &cache_.k##Name##AnyOperator; \ return &cache_.k##Name##AnyOperator; \
} \ } \
......
...@@ -90,9 +90,8 @@ class JSSpeculativeBinopBuilder final { ...@@ -90,9 +90,8 @@ 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::kReceiverOrNullOrUndefined:
case CompareOperationHint::kInternalizedString: case CompareOperationHint::kInternalizedString:
break; break;
} }
......
...@@ -43,21 +43,15 @@ class JSBinopReduction final { ...@@ -43,21 +43,15 @@ class JSBinopReduction final {
*hint = NumberOperationHint::kNumber; *hint = NumberOperationHint::kNumber;
return true; return true;
case CompareOperationHint::kNumberOrOddball: case CompareOperationHint::kNumberOrOddball:
if (node_->opcode() != IrOpcode::kJSStrictEqual) { *hint = NumberOperationHint::kNumberOrOddball;
// Number equality comparisons don't truncate Oddball return true;
// 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::kReceiverOrNullOrUndefined:
case CompareOperationHint::kInternalizedString: case CompareOperationHint::kInternalizedString:
break; break;
} }
...@@ -71,13 +65,6 @@ class JSBinopReduction final { ...@@ -71,13 +65,6 @@ 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()) ==
...@@ -85,11 +72,11 @@ class JSBinopReduction final { ...@@ -85,11 +72,11 @@ class JSBinopReduction final {
BothInputsMaybe(Type::Receiver()); BothInputsMaybe(Type::Receiver());
} }
bool IsReceiverOrOddballCompareOperation() { bool IsReceiverOrNullOrUndefinedCompareOperation() {
DCHECK_EQ(1, node_->op()->EffectOutputCount()); DCHECK_EQ(1, node_->op()->EffectOutputCount());
return (CompareOperationHintOf(node_->op()) == return (CompareOperationHintOf(node_->op()) ==
CompareOperationHint::kReceiverOrOddball) && CompareOperationHint::kReceiverOrNullOrUndefined) &&
BothInputsMaybe(Type::ReceiverOrOddball()); BothInputsMaybe(Type::ReceiverOrNullOrUndefined());
} }
bool IsStringCompareOperation() { bool IsStringCompareOperation() {
...@@ -135,14 +122,6 @@ class JSBinopReduction final { ...@@ -135,14 +122,6 @@ 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(),
...@@ -151,10 +130,11 @@ class JSBinopReduction final { ...@@ -151,10 +130,11 @@ class JSBinopReduction final {
update_effect(left_input); update_effect(left_input);
} }
// Inserts a CheckReceiverOrOddball for the left input. // Inserts a CheckReceiverOrNullOrUndefined for the left input.
void CheckLeftInputToReceiverOrOddball() { void CheckLeftInputToReceiverOrNullOrUndefined() {
Node* left_input = graph()->NewNode(simplified()->CheckReceiverOrOddball(), Node* left_input =
left(), effect(), control()); graph()->NewNode(simplified()->CheckReceiverOrNullOrUndefined(), left(),
effect(), control());
node_->ReplaceInput(0, left_input); node_->ReplaceInput(0, left_input);
update_effect(left_input); update_effect(left_input);
} }
...@@ -174,6 +154,22 @@ class JSBinopReduction final { ...@@ -174,6 +154,22 @@ class JSBinopReduction final {
} }
} }
// Checks that both inputs are Receiver, Null or Undefined and if
// we don't know statically that one side is already a Receiver,
// Null or Undefined, insert CheckReceiverOrNullOrUndefined nodes.
void CheckInputsToReceiverOrNullOrUndefined() {
if (!left_type().Is(Type::ReceiverOrNullOrUndefined())) {
CheckLeftInputToReceiverOrNullOrUndefined();
}
if (!right_type().Is(Type::ReceiverOrNullOrUndefined())) {
Node* right_input =
graph()->NewNode(simplified()->CheckReceiverOrNullOrUndefined(),
right(), effect(), control());
node_->ReplaceInput(1, right_input);
update_effect(right_input);
}
}
// Inserts a CheckSymbol for the left input. // Inserts a CheckSymbol for the left input.
void CheckLeftInputToSymbol() { void CheckLeftInputToSymbol() {
Node* left_input = graph()->NewNode(simplified()->CheckSymbol(), left(), Node* left_input = graph()->NewNode(simplified()->CheckSymbol(), left(),
...@@ -858,6 +854,46 @@ Reduction JSTypedLowering::ReduceJSEqual(Node* node) { ...@@ -858,6 +854,46 @@ Reduction JSTypedLowering::ReduceJSEqual(Node* node) {
} else if (r.IsReceiverCompareOperation()) { } else if (r.IsReceiverCompareOperation()) {
r.CheckInputsToReceiver(); r.CheckInputsToReceiver();
return r.ChangeToPureOperator(simplified()->ReferenceEqual()); return r.ChangeToPureOperator(simplified()->ReferenceEqual());
} else if (r.IsReceiverOrNullOrUndefinedCompareOperation()) {
// Check that both inputs are Receiver, Null or Undefined.
r.CheckInputsToReceiverOrNullOrUndefined();
// If one side is known to be a detectable receiver now, we
// can simply perform reference equality here, since this
// known detectable receiver is going to only match itself.
if (r.OneInputIs(Type::DetectableReceiver())) {
return r.ChangeToPureOperator(simplified()->ReferenceEqual());
}
// Known that both sides are Receiver, Null or Undefined, the
// abstract equality operation can be performed like this:
//
// if ObjectIsUndetectable(left)
// then ObjectIsUndetectable(right)
// else ReferenceEqual(left, right)
//
Node* left = r.left();
Node* right = r.right();
Node* effect = r.effect();
Node* control = r.control();
Node* check = graph()->NewNode(simplified()->ObjectIsUndetectable(), left);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue = graph()->NewNode(simplified()->ObjectIsUndetectable(), right);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse =
graph()->NewNode(simplified()->ReferenceEqual(), left, right);
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue, vfalse, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
} else if (r.IsStringCompareOperation()) { } else if (r.IsStringCompareOperation()) {
r.CheckInputsToString(); r.CheckInputsToString();
return r.ChangeToPureOperator(simplified()->StringEqual()); return r.ChangeToPureOperator(simplified()->StringEqual());
...@@ -912,23 +948,18 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node) { ...@@ -912,23 +948,18 @@ 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()) { } else if (r.IsReceiverOrNullOrUndefinedCompareOperation()) {
// 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,
// or an Oddball, as a strict equality comparison with a Receiver and/or // Null or Undefined, as a strict equality comparison with a Receiver,
// Oddball can only yield true if both sides refer to the same instance. // Null or Undefined can only yield true if both sides refer to the same
r.CheckLeftInputToReceiverOrOddball(); // instance.
r.CheckLeftInputToReceiverOrNullOrUndefined();
return r.ChangeToPureOperator(simplified()->ReferenceEqual()); return r.ChangeToPureOperator(simplified()->ReferenceEqual());
} else if (r.IsStringCompareOperation()) { } else if (r.IsStringCompareOperation()) {
r.CheckInputsToString(); r.CheckInputsToString();
......
...@@ -379,9 +379,8 @@ ...@@ -379,9 +379,8 @@
V(CheckMaps) \ V(CheckMaps) \
V(CheckNumber) \ V(CheckNumber) \
V(CheckInternalizedString) \ V(CheckInternalizedString) \
V(CheckOddball) \
V(CheckReceiver) \ V(CheckReceiver) \
V(CheckReceiverOrOddball) \ V(CheckReceiverOrNullOrUndefined) \
V(CheckString) \ V(CheckString) \
V(CheckSymbol) \ V(CheckSymbol) \
V(CheckSmi) \ V(CheckSmi) \
......
...@@ -28,9 +28,8 @@ Reduction RedundancyElimination::Reduce(Node* node) { ...@@ -28,9 +28,8 @@ 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::kCheckReceiverOrNullOrUndefined:
case IrOpcode::kCheckSmi: case IrOpcode::kCheckSmi:
case IrOpcode::kCheckString: case IrOpcode::kCheckString:
case IrOpcode::kCheckSymbol: case IrOpcode::kCheckSymbol:
...@@ -137,12 +136,9 @@ bool CheckSubsumes(Node const* a, Node const* b) { ...@@ -137,12 +136,9 @@ 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 && } else if (a->opcode() == IrOpcode::kCheckReceiver &&
b->opcode() == IrOpcode::kCheckReceiverOrOddball) { b->opcode() == IrOpcode::kCheckReceiverOrNullOrUndefined) {
// CheckReceiver(node) implies CheckReceiverOrOddball(node) // CheckReceiver(node) implies CheckReceiverOrNullOrUndefined(node)
} else if (a->opcode() != b->opcode()) { } else if (a->opcode() != b->opcode()) {
return false; return false;
} else { } else {
......
...@@ -2599,16 +2599,12 @@ class RepresentationSelector { ...@@ -2599,16 +2599,12 @@ 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: { case IrOpcode::kCheckReceiverOrNullOrUndefined: {
VisitCheck(node, Type::ReceiverOrOddball(), lowering); VisitCheck(node, Type::ReceiverOrNullOrUndefined(), lowering);
return; return;
} }
case IrOpcode::kCheckSmi: { case IrOpcode::kCheckSmi: {
......
...@@ -782,21 +782,20 @@ bool operator==(CheckMinusZeroParameters const& lhs, ...@@ -782,21 +782,20 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(SpeculativeNumberLessThan) \ V(SpeculativeNumberLessThan) \
V(SpeculativeNumberLessThanOrEqual) V(SpeculativeNumberLessThanOrEqual)
#define CHECKED_OP_LIST(V) \ #define CHECKED_OP_LIST(V) \
V(CheckEqualsInternalizedString, 2, 0) \ V(CheckEqualsInternalizedString, 2, 0) \
V(CheckEqualsSymbol, 2, 0) \ V(CheckEqualsSymbol, 2, 0) \
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(CheckReceiverOrNullOrUndefined, 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) \ V(CheckedInt32Mod, 2, 1) \
V(CheckedInt32Mod, 2, 1) \ V(CheckedInt32Sub, 2, 1) \
V(CheckedInt32Sub, 2, 1) \ V(CheckedUint32Div, 2, 1) \
V(CheckedUint32Div, 2, 1) \
V(CheckedUint32Mod, 2, 1) V(CheckedUint32Mod, 2, 1)
#define CHECKED_WITH_FEEDBACK_OP_LIST(V) \ #define CHECKED_WITH_FEEDBACK_OP_LIST(V) \
......
...@@ -678,9 +678,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -678,9 +678,8 @@ 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* CheckReceiverOrNullOrUndefined();
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,19 +2003,14 @@ Type Typer::Visitor::TypeCheckNumber(Node* node) { ...@@ -2003,19 +2003,14 @@ 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 Typer::Visitor::TypeCheckReceiverOrNullOrUndefined(Node* node) {
Type arg = Operand(node, 0); Type arg = Operand(node, 0);
return Type::Intersect(arg, Type::ReceiverOrOddball(), zone()); return Type::Intersect(arg, Type::ReceiverOrNullOrUndefined(), zone());
} }
Type Typer::Visitor::TypeCheckSmi(Node* node) { Type Typer::Visitor::TypeCheckSmi(Node* node) {
......
...@@ -188,7 +188,6 @@ namespace compiler { ...@@ -188,7 +188,6 @@ 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,17 +1450,13 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -1450,17 +1450,13 @@ 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: case IrOpcode::kCheckReceiverOrNullOrUndefined:
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::ReceiverOrOddball()); CheckTypeIs(node, Type::ReceiverOrNullOrUndefined());
break; break;
case IrOpcode::kCheckSmi: case IrOpcode::kCheckSmi:
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
......
...@@ -39,9 +39,9 @@ namespace internal { ...@@ -39,9 +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(NotAJavaScriptObjectOrNullOrUndefined, \
"not a JavaScript object, Null or Undefined") \
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,8 +213,6 @@ CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) { ...@@ -213,8 +213,6 @@ 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:
...@@ -227,8 +225,8 @@ CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) { ...@@ -227,8 +225,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: case CompareOperationFeedback::kReceiverOrNullOrUndefined:
return CompareOperationHint::kReceiverOrOddball; return CompareOperationHint::kReceiverOrNullOrUndefined;
default: default:
return CompareOperationHint::kAny; return CompareOperationHint::kAny;
} }
......
...@@ -1309,31 +1309,28 @@ class BinaryOperationFeedback { ...@@ -1309,31 +1309,28 @@ 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
// kOddball -> kNumberOrOddball -> kAny // kReceiver -> kReceiverOrNullOrUndefined -> kAny
// kOddball -> kReceiverOrOddball -> kAny // kInternalizedString -> kString -> kAny
// kReceiver -> kReceiverOrOddball -> kAny // kSymbol -> kAny
// kInternalizedString -> kString -> kAny // kBigInt -> 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.
class CompareOperationFeedback { class CompareOperationFeedback {
public: public:
enum { enum {
kNone = 0x00, kNone = 0x000,
kSignedSmall = 0x01, kSignedSmall = 0x001,
kNumber = 0x3, kNumber = 0x003,
kOddball = 0x04, kNumberOrOddball = 0x007,
kNumberOrOddball = kNumber | kOddball, kInternalizedString = 0x008,
kInternalizedString = 0x8, kString = 0x018,
kString = 0x18, kSymbol = 0x020,
kSymbol = 0x20, kBigInt = 0x040,
kBigInt = 0x30, kReceiver = 0x080,
kReceiver = 0x40, kReceiverOrNullOrUndefined = 0x180,
kReceiverOrOddball = kReceiver | kOddball, kAny = 0x1ff
kAny = 0xff
}; };
}; };
......
...@@ -39,8 +39,6 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) { ...@@ -39,8 +39,6 @@ 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:
...@@ -53,8 +51,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) { ...@@ -53,8 +51,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: case CompareOperationHint::kReceiverOrNullOrUndefined:
return os << "ReceiverOrOddball"; return os << "ReceiverOrNullOrUndefined";
case CompareOperationHint::kAny: case CompareOperationHint::kAny:
return os << "Any"; return os << "Any";
} }
......
...@@ -35,14 +35,13 @@ enum class CompareOperationHint : uint8_t { ...@@ -35,14 +35,13 @@ 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, kReceiverOrNullOrUndefined,
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 --opt --noalways-opt
// Known receivers abstract equality.
(function() {
const a = {};
const b = {};
function foo() { return a == b; }
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
// Known receiver/null abstract equality.
(function() {
const a = {};
const b = null;
function foo() { return a == b; }
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
// Known null/receiver abstract equality.
(function() {
const a = null;
const b = {};
function foo() { return a == b; }
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
// Known receiver/undefined abstract equality.
(function() {
const a = {};
const b = undefined;
function foo() { return a == b; }
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
// Known undefined/receiver abstract equality.
(function() {
const a = undefined;
const b = {};
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));
// TurboFan bakes in feedback for the (unknown) left hand side.
assertFalse(foo(null));
assertUnoptimized(foo);
})();
// Known receiver on one side strict equality with null.
(function() {
const a = null;
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));
// TurboFan bakes in feedback for the (unknown) left hand side.
assertFalse(foo(1));
assertUnoptimized(foo);
})();
// Known receiver on one side strict equality with undefined.
(function() {
const a = undefined;
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));
// TurboFan bakes in feedback for the (unknown) left hand side.
assertFalse(foo(1));
assertUnoptimized(foo);
})();
// Known null on one side strict equality with receiver.
(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));
assertTrue(foo(null));
assertTrue(foo(undefined));
assertOptimized(foo);
// TurboFan doesn't need to bake in feedback, since it sees the null.
assertFalse(foo(1));
assertOptimized(foo);
})();
// Known undefined on one side strict equality with receiver.
(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));
assertTrue(foo(null));
assertTrue(foo(undefined));
assertOptimized(foo);
// TurboFan needs to bake in feedback, since undefined cannot
// be context specialized.
assertFalse(foo(1));
assertUnoptimized(foo);
})();
...@@ -4,12 +4,14 @@ ...@@ -4,12 +4,14 @@
// Flags: --allow-natives-syntax --opt --noalways-opt // Flags: --allow-natives-syntax --opt --noalways-opt
// Known oddballs strict equality. const undetectable = %GetUndetectable();
// Known undetectable abstract equality.
(function() { (function() {
const a = null; const a = undetectable;
const b = undefined; const b = {};
function foo() { return a === b; } function foo() { return a == b; }
assertFalse(foo()); assertFalse(foo());
assertFalse(foo()); assertFalse(foo());
...@@ -17,12 +19,64 @@ ...@@ -17,12 +19,64 @@
assertFalse(foo()); assertFalse(foo());
})(); })();
// Known oddball on one side strict equality. // Known undetectable/null abstract equality.
(function() {
const a = undetectable;
const b = null;
function foo() { return a == b; }
assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
})();
// Known undetectable/receiver abstract equality.
(function() {
const a = null;
const b = undetectable;
function foo() { return a == b; }
assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
})();
// Known undetectable/undefined abstract equality.
(function() {
const a = undetectable;
const b = undefined;
function foo() { return a == b; }
assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
})();
// Known undefined/undetectable abstract equality.
(function() { (function() {
const a = true; const a = undefined;
const b = false; const b = undetectable;
function foo() { return a == b; }
function foo(a) { return a === b; } assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
})();
// Known undetectable on one side strict equality with receiver.
(function() {
const a = {};
const b = undetectable;
function foo(a) { return a == b; }
assertTrue(foo(b)); assertTrue(foo(b));
assertFalse(foo(a)); assertFalse(foo(a));
...@@ -31,24 +85,35 @@ ...@@ -31,24 +85,35 @@
%OptimizeFunctionOnNextCall(foo); %OptimizeFunctionOnNextCall(foo);
assertTrue(foo(b)); assertTrue(foo(b));
assertFalse(foo(a)); assertFalse(foo(a));
// TurboFan doesn't need to bake in feedback, since it sees the undetectable.
assertFalse(foo(1));
assertOptimized(foo);
})(); })();
// Feedback based oddball strict equality. // Unknown undetectable on one side strict equality with receiver.
(function() { (function() {
const a = null; const a = undetectable;
const b = undefined; const b = {};
function foo(a, b) { return a === b; } function foo(a, b) { return a == b; }
assertTrue(foo(b, b)); assertTrue(foo(b, b));
assertFalse(foo(a, b)); assertFalse(foo(a, b));
assertTrue(foo(a, a)); assertTrue(foo(a, a));
assertFalse(foo(b, a)); assertFalse(foo(b, a));
assertTrue(foo(a, null));
assertFalse(foo(b, null));
%OptimizeFunctionOnNextCall(foo); %OptimizeFunctionOnNextCall(foo);
assertTrue(foo(b, b));
assertFalse(foo(a, b));
assertTrue(foo(a, a)); assertTrue(foo(a, a));
assertFalse(foo(b, a)); assertFalse(foo(b, a));
assertTrue(foo(a, null));
assertFalse(foo(b, null));
assertOptimized(foo);
// TurboFan bakes in feedback for the left hand side. // TurboFan bakes in feedback on the inputs.
assertFalse(foo({}, b)); assertFalse(foo(1));
assertUnoptimized(foo); assertUnoptimized(foo);
})(); })();
...@@ -132,27 +132,6 @@ TEST_F(RedundancyEliminationTest, CheckNumberSubsumedByCheckSmi) { ...@@ -132,27 +132,6 @@ 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 // CheckReceiver
...@@ -175,47 +154,28 @@ TEST_F(RedundancyEliminationTest, CheckReceiver) { ...@@ -175,47 +154,28 @@ TEST_F(RedundancyEliminationTest, CheckReceiver) {
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// CheckReceiverOrOddball // CheckReceiverOrNullOrUndefined
TEST_F(RedundancyEliminationTest, CheckReceiverOrOddball) { TEST_F(RedundancyEliminationTest, CheckReceiverOrNullOrUndefined) {
Node* value = Parameter(0); Node* value = Parameter(0);
Node* effect = graph()->start(); Node* effect = graph()->start();
Node* control = graph()->start(); Node* control = graph()->start();
Node* check1 = effect = graph()->NewNode( Node* check1 = effect = graph()->NewNode(
simplified()->CheckReceiverOrOddball(), value, effect, control); simplified()->CheckReceiverOrNullOrUndefined(), 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); Reduction r1 = Reduce(check1);
ASSERT_TRUE(r1.Changed()); ASSERT_TRUE(r1.Changed());
EXPECT_EQ(r1.replacement(), check1); EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect = graph()->NewNode( Node* check2 = effect = graph()->NewNode(
simplified()->CheckReceiverOrOddball(), value, effect, control); simplified()->CheckReceiverOrNullOrUndefined(), value, effect, control);
Reduction r2 = Reduce(check2); Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed()); ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1); EXPECT_EQ(r2.replacement(), check1);
} }
TEST_F(RedundancyEliminationTest, TEST_F(RedundancyEliminationTest,
CheckReceiverOrOddballSubsumedByCheckReceiver) { CheckReceiverOrNullOrUndefinedSubsumedByCheckReceiver) {
Node* value = Parameter(0); Node* value = Parameter(0);
Node* effect = graph()->start(); Node* effect = graph()->start();
Node* control = graph()->start(); Node* control = graph()->start();
...@@ -227,7 +187,7 @@ TEST_F(RedundancyEliminationTest, ...@@ -227,7 +187,7 @@ TEST_F(RedundancyEliminationTest,
EXPECT_EQ(r1.replacement(), check1); EXPECT_EQ(r1.replacement(), check1);
Node* check2 = effect = graph()->NewNode( Node* check2 = effect = graph()->NewNode(
simplified()->CheckReceiverOrOddball(), value, effect, control); simplified()->CheckReceiverOrNullOrUndefined(), value, effect, control);
Reduction r2 = Reduce(check2); Reduction r2 = Reduce(check2);
ASSERT_TRUE(r2.Changed()); ASSERT_TRUE(r2.Changed());
EXPECT_EQ(r2.replacement(), check1); EXPECT_EQ(r2.replacement(), check1);
......
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