[cleanup] Simplify CodeStubAssembler::Equals

Restructuring the code a bit, dropping unnecessary labels and
trivial comments. Replacing the InstanceType jump table with
a few comparisons is faster and smaller.

No change in functionality intended.

Bug: v8:6921
Change-Id: Ia8c751717c0dbcd6a664ca508ddeff898cd84359
Reviewed-on: https://chromium-review.googlesource.com/729466
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48769}
This commit is contained in:
Jakob Kummerow 2017-10-19 18:47:00 -07:00 committed by Commit Bot
parent 7a18e9af69
commit 52d8c2a422

View File

@ -8427,114 +8427,98 @@ void CodeStubAssembler::GenerateEqual_Same(Node* value, Label* if_equal,
// for NaN values because they are not considered equal, even if both the // for NaN values because they are not considered equal, even if both the
// left and the right hand side reference exactly the same value. // left and the right hand side reference exactly the same value.
// Check if {value} is a Smi or a HeapObject. Label if_smi(this), if_heapnumber(this);
Label if_valueissmi(this), if_valueisnotsmi(this); GotoIf(TaggedIsSmi(value), &if_smi);
Branch(TaggedIsSmi(value), &if_valueissmi, &if_valueisnotsmi);
BIND(&if_valueisnotsmi); Node* value_map = LoadMap(value);
{ GotoIf(IsHeapNumberMap(value_map), &if_heapnumber);
// Load the map of {value}.
Node* value_map = LoadMap(value);
// Check if {value} (and therefore {rhs}) is a HeapNumber. // For non-HeapNumbers, all we do is collect type feedback.
Label if_valueisnumber(this), if_valueisnotnumber(this); if (var_type_feedback != nullptr) {
Branch(IsHeapNumberMap(value_map), &if_valueisnumber, &if_valueisnotnumber); Node* instance_type = LoadMapInstanceType(value_map);
BIND(&if_valueisnumber); Label if_string(this), if_receiver(this), if_symbol(this),
if_other(this, Label::kDeferred);
GotoIf(IsStringInstanceType(instance_type), &if_string);
GotoIf(IsJSReceiverInstanceType(instance_type), &if_receiver);
Branch(IsSymbolInstanceType(instance_type), &if_symbol, &if_other);
BIND(&if_string);
{ {
if (var_type_feedback != nullptr) { CombineFeedback(var_type_feedback,
CombineFeedback(var_type_feedback, CollectFeedbackForString(instance_type));
SmiConstant(CompareOperationFeedback::kNumber));
}
// Convert {value} (and therefore {rhs}) to floating point value.
Node* value_value = LoadHeapNumberValue(value);
// Check if the HeapNumber value is a NaN.
BranchIfFloat64IsNaN(value_value, if_notequal, if_equal);
}
BIND(&if_valueisnotnumber);
if (var_type_feedback != nullptr) {
// Collect type feedback.
Node* instance_type = LoadMapInstanceType(value_map);
Label if_valueisstring(this), if_valueisreceiver(this),
if_valueissymbol(this), if_valueisother(this, Label::kDeferred);
GotoIf(IsStringInstanceType(instance_type), &if_valueisstring);
GotoIf(IsJSReceiverInstanceType(instance_type), &if_valueisreceiver);
Branch(IsSymbolInstanceType(instance_type), &if_valueissymbol,
&if_valueisother);
BIND(&if_valueisstring);
{
CombineFeedback(var_type_feedback,
CollectFeedbackForString(instance_type));
Goto(if_equal);
}
BIND(&if_valueissymbol);
{
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kSymbol));
Goto(if_equal);
}
BIND(&if_valueisreceiver);
{
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kReceiver));
Goto(if_equal);
}
// TODO(neis): Introduce BigInt CompareOperationFeedback and collect here
// and elsewhere?
BIND(&if_valueisother);
{
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kAny));
Goto(if_equal);
}
} else {
Goto(if_equal); Goto(if_equal);
} }
BIND(&if_symbol);
{
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kSymbol));
Goto(if_equal);
}
BIND(&if_receiver);
{
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kReceiver));
Goto(if_equal);
}
// TODO(neis): Introduce BigInt CompareOperationFeedback and collect here
// and elsewhere?
BIND(&if_other);
{
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kAny));
Goto(if_equal);
}
} else {
Goto(if_equal);
} }
BIND(&if_valueissmi); BIND(&if_heapnumber);
if (var_type_feedback != nullptr) { {
CombineFeedback(var_type_feedback, if (var_type_feedback != nullptr) {
SmiConstant(CompareOperationFeedback::kSignedSmall)); CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kNumber));
}
Node* number_value = LoadHeapNumberValue(value);
BranchIfFloat64IsNaN(number_value, if_notequal, if_equal);
}
BIND(&if_smi);
{
if (var_type_feedback != nullptr) {
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kSignedSmall));
}
Goto(if_equal);
} }
Goto(if_equal);
} }
// ES6 section 7.2.12 Abstract Equality Comparison // ES6 section 7.2.12 Abstract Equality Comparison
Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, Node* CodeStubAssembler::Equal(Node* left, Node* right, Node* context,
Variable* var_type_feedback) { Variable* var_type_feedback) {
// This is a slightly optimized version of Object::Equals represented as // This is a slightly optimized version of Object::Equals. Whenever you
// scheduled TurboFan graph utilizing the CodeStubAssembler. Whenever you
// change something functionality wise in here, remember to update the // change something functionality wise in here, remember to update the
// Object::Equals method as well. // Object::Equals method as well.
Label if_equal(this), if_notequal(this), Label if_equal(this), if_notequal(this), do_float_comparison(this),
do_rhsstringtonumber(this, Label::kDeferred), end(this); do_right_stringtonumber(this, Label::kDeferred), end(this);
VARIABLE(result, MachineRepresentation::kTagged); VARIABLE(result, MachineRepresentation::kTagged);
TVARIABLE(Float64T, var_left_float);
TVARIABLE(Float64T, var_right_float);
// We can avoid code duplication by exploiting the fact that abstract equality // We can avoid code duplication by exploiting the fact that abstract equality
// is symmetric. // is symmetric.
Label use_symmetry(this); Label use_symmetry(this);
// Shared entry for floating point comparison.
Label do_fcmp(this);
VARIABLE(var_fcmp_lhs, MachineRepresentation::kFloat64);
VARIABLE(var_fcmp_rhs, MachineRepresentation::kFloat64);
// We might need to loop several times due to ToPrimitive and/or ToNumber // We might need to loop several times due to ToPrimitive and/or ToNumber
// conversions. // conversions.
VARIABLE(var_lhs, MachineRepresentation::kTagged, lhs); VARIABLE(var_left, MachineRepresentation::kTagged, left);
VARIABLE(var_rhs, MachineRepresentation::kTagged, rhs); VARIABLE(var_right, MachineRepresentation::kTagged, right);
VariableList loop_variable_list({&var_lhs, &var_rhs}, zone()); VariableList loop_variable_list({&var_left, &var_right}, zone());
if (var_type_feedback != nullptr) { if (var_type_feedback != nullptr) {
// Initialize the type feedback to None. The current feedback is combined // Initialize the type feedback to None. The current feedback is combined
// with the previous feedback. // with the previous feedback.
@ -8545,440 +8529,303 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
Goto(&loop); Goto(&loop);
BIND(&loop); BIND(&loop);
{ {
// Load the current {lhs} and {rhs} values. left = var_left.value();
lhs = var_lhs.value(); right = var_right.value();
rhs = var_rhs.value();
// Check if {lhs} and {rhs} refer to the same object. Label if_notsame(this);
Label if_same(this), if_notsame(this); GotoIf(WordNotEqual(left, right), &if_notsame);
Branch(WordEqual(lhs, rhs), &if_same, &if_notsame);
BIND(&if_same);
{ {
// The {lhs} and {rhs} reference the exact same value, yet we need special // {left} and {right} reference the exact same value, yet we need special
// treatment for HeapNumber, as NaN is not equal to NaN. // treatment for HeapNumber, as NaN is not equal to NaN.
GenerateEqual_Same(lhs, &if_equal, &if_notequal, var_type_feedback); GenerateEqual_Same(left, &if_equal, &if_notequal, var_type_feedback);
} }
BIND(&if_notsame); BIND(&if_notsame);
Label if_left_smi(this), if_left_not_smi(this);
Branch(TaggedIsSmi(left), &if_left_smi, &if_left_not_smi);
BIND(&if_left_smi);
{ {
// Check if {lhs} is a Smi or a HeapObject. Label if_right_smi(this), if_right_not_smi(this);
Label if_lhsissmi(this), if_lhsisnotsmi(this); Branch(TaggedIsSmi(right), &if_right_smi, &if_right_not_smi);
Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi);
BIND(&if_lhsissmi); BIND(&if_right_smi);
{ {
// Check if {rhs} is a Smi or a HeapObject. // We have already checked for {left} and {right} being the same value,
Label if_rhsissmi(this), if_rhsisnotsmi(this); // so when we get here they must be different Smis.
Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
BIND(&if_rhsissmi);
// We have already checked for {lhs} and {rhs} being the same value, so
// if both are Smis when we get here they must not be equal.
if (var_type_feedback != nullptr) { if (var_type_feedback != nullptr) {
CombineFeedback(var_type_feedback, CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kSignedSmall)); SmiConstant(CompareOperationFeedback::kSignedSmall));
} }
Goto(&if_notequal); Goto(&if_notequal);
}
BIND(&if_rhsisnotsmi); BIND(&if_right_not_smi);
Node* right_map = LoadMap(right);
Label if_right_heapnumber(this), if_right_boolean(this),
if_right_bigint(this, Label::kDeferred),
if_right_receiver(this, Label::kDeferred);
GotoIf(IsHeapNumberMap(right_map), &if_right_heapnumber);
// {left} is Smi and {right} is not HeapNumber or Smi.
if (var_type_feedback != nullptr) {
var_type_feedback->Bind(SmiConstant(CompareOperationFeedback::kAny));
}
GotoIf(IsBooleanMap(right_map), &if_right_boolean);
Node* right_type = LoadMapInstanceType(right_map);
GotoIf(IsStringInstanceType(right_type), &do_right_stringtonumber);
GotoIf(IsBigIntInstanceType(right_type), &if_right_bigint);
Branch(IsJSReceiverInstanceType(right_type), &if_right_receiver,
&if_notequal);
BIND(&if_right_heapnumber);
{
var_left_float = SmiToFloat64(left);
var_right_float = LoadHeapNumberValue(right);
if (var_type_feedback != nullptr) {
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kNumber));
}
Goto(&do_float_comparison);
}
BIND(&if_right_boolean);
{
var_right.Bind(LoadObjectField(right, Oddball::kToNumberOffset));
Goto(&loop);
}
BIND(&if_right_bigint);
{
result.Bind(CallRuntime(Runtime::kBigIntEqualToNumber,
NoContextConstant(), right, left));
Goto(&end);
}
BIND(&if_right_receiver);
{
Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate());
var_right.Bind(CallStub(callable, context, right));
Goto(&loop);
}
}
BIND(&if_left_not_smi);
{
GotoIf(TaggedIsSmi(right), &use_symmetry);
Label if_left_symbol(this), if_left_number(this), if_left_string(this),
if_left_bigint(this, Label::kDeferred), if_left_oddball(this),
if_left_receiver(this);
Node* left_map = LoadMap(left);
Node* right_map = LoadMap(right);
Node* left_type = LoadMapInstanceType(left_map);
Node* right_type = LoadMapInstanceType(right_map);
GotoIf(Int32LessThan(left_type, Int32Constant(FIRST_NONSTRING_TYPE)),
&if_left_string);
GotoIf(InstanceTypeEqual(left_type, SYMBOL_TYPE), &if_left_symbol);
GotoIf(InstanceTypeEqual(left_type, HEAP_NUMBER_TYPE), &if_left_number);
GotoIf(InstanceTypeEqual(left_type, ODDBALL_TYPE), &if_left_oddball);
GotoIf(InstanceTypeEqual(left_type, BIGINT_TYPE), &if_left_bigint);
Goto(&if_left_receiver);
BIND(&if_left_string);
{
GotoIfNot(IsStringInstanceType(right_type), &use_symmetry);
result.Bind(CallBuiltin(Builtins::kStringEqual, context, left, right));
if (var_type_feedback != nullptr) {
CombineFeedback(var_type_feedback,
SmiOr(CollectFeedbackForString(left_type),
CollectFeedbackForString(right_type)));
}
Goto(&end);
}
BIND(&if_left_number);
{
Label if_right_not_number(this);
GotoIf(Word32NotEqual(left_type, right_type), &if_right_not_number);
var_left_float = LoadHeapNumberValue(left);
var_right_float = LoadHeapNumberValue(right);
if (var_type_feedback != nullptr) {
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kNumber));
}
Goto(&do_float_comparison);
BIND(&if_right_not_number);
{ {
// Load the map of {rhs}. Label if_right_boolean(this);
Node* rhs_map = LoadMap(rhs); if (var_type_feedback != nullptr) {
var_type_feedback->Bind(
// Check if {rhs} is a HeapNumber. SmiConstant(CompareOperationFeedback::kAny));
Label if_rhsisnumber(this), if_rhsisnotnumber(this);
Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber);
BIND(&if_rhsisnumber);
{
// Convert {lhs} and {rhs} to floating point values, and
// perform a floating point comparison.
var_fcmp_lhs.Bind(SmiToFloat64(lhs));
var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs));
if (var_type_feedback != nullptr) {
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kNumber));
}
Goto(&do_fcmp);
} }
GotoIf(IsStringInstanceType(right_type), &do_right_stringtonumber);
GotoIf(IsBooleanMap(right_map), &if_right_boolean);
GotoIf(IsBigIntInstanceType(right_type), &use_symmetry);
Branch(IsJSReceiverInstanceType(right_type), &use_symmetry,
&if_notequal);
BIND(&if_rhsisnotnumber); BIND(&if_right_boolean);
{ {
// The {lhs} is Smi and {rhs} is not HeapNumber or Smi. var_right.Bind(LoadObjectField(right, Oddball::kToNumberOffset));
if (var_type_feedback != nullptr) { Goto(&loop);
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny));
}
// Further inspect {rhs}.
Label if_rhsisstring(this, Label::kDeferred), if_rhsisboolean(this),
if_rhsisbigint(this, Label::kDeferred),
if_rhsisreceiver(this, Label::kDeferred);
Node* rhs_instance_type = LoadMapInstanceType(rhs_map);
GotoIf(IsStringInstanceType(rhs_instance_type), &if_rhsisstring);
GotoIf(IsBooleanMap(rhs_map), &if_rhsisboolean);
GotoIf(IsBigIntInstanceType(rhs_instance_type), &if_rhsisbigint);
Branch(IsJSReceiverInstanceType(rhs_instance_type),
&if_rhsisreceiver, &if_notequal);
BIND(&if_rhsisstring);
{
// The {rhs} is a String and the {lhs} is a Smi; we need
// to convert the {rhs} to a Number and compare the output to
// the Number on the {lhs}.
Goto(&do_rhsstringtonumber);
}
BIND(&if_rhsisboolean);
{
// The {rhs} is a Boolean, load its number value.
var_rhs.Bind(LoadObjectField(rhs, Oddball::kToNumberOffset));
Goto(&loop);
}
BIND(&if_rhsisbigint);
{
result.Bind(CallRuntime(Runtime::kBigIntEqualToNumber,
NoContextConstant(), rhs, lhs));
Goto(&end);
}
BIND(&if_rhsisreceiver);
{
// Convert {rhs} to a primitive first (passing no hint).
Callable callable =
CodeFactory::NonPrimitiveToPrimitive(isolate());
var_rhs.Bind(CallStub(callable, context, rhs));
Goto(&loop);
}
} }
} }
} }
BIND(&if_lhsisnotsmi); BIND(&if_left_bigint);
{ {
GotoIf(TaggedIsSmi(rhs), &use_symmetry); if (var_type_feedback != nullptr) {
var_type_feedback->Bind(SmiConstant(CompareOperationFeedback::kAny));
// {rhs} is a HeapObject. Further inspect {lhs}.
Label if_lhsissymbol(this), if_lhsisheapnumber(this),
if_lhsisstring(this), if_lhsisbigint(this, Label::kDeferred),
if_lhsisoddball(this), if_lhsisreceiver(this);
// Both {lhs} and {rhs} are HeapObjects, load their maps
// and their instance types.
Node* lhs_map = LoadMap(lhs);
Node* rhs_map = LoadMap(rhs);
// Load the instance types of {lhs} and {rhs}.
Node* lhs_instance_type = LoadMapInstanceType(lhs_map);
Node* rhs_instance_type = LoadMapInstanceType(rhs_map);
// Dispatch based on the instance type of {lhs}.
size_t const kNumCases = FIRST_NONSTRING_TYPE + 4;
Label* case_labels[kNumCases];
int32_t case_values[kNumCases];
for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) {
case_labels[i] = new Label(this);
case_values[i] = i;
}
case_values[FIRST_NONSTRING_TYPE + 0] = SYMBOL_TYPE;
case_labels[FIRST_NONSTRING_TYPE + 0] = &if_lhsissymbol;
case_values[FIRST_NONSTRING_TYPE + 1] = HEAP_NUMBER_TYPE;
case_labels[FIRST_NONSTRING_TYPE + 1] = &if_lhsisheapnumber;
case_values[FIRST_NONSTRING_TYPE + 2] = BIGINT_TYPE;
case_labels[FIRST_NONSTRING_TYPE + 2] = &if_lhsisbigint;
case_values[FIRST_NONSTRING_TYPE + 3] = ODDBALL_TYPE;
case_labels[FIRST_NONSTRING_TYPE + 3] = &if_lhsisoddball;
Switch(lhs_instance_type, &if_lhsisreceiver, case_values, case_labels,
arraysize(case_values));
for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) {
BIND(case_labels[i]);
Goto(&if_lhsisstring);
delete case_labels[i];
} }
BIND(&if_lhsisstring); Label if_right_heapnumber(this), if_right_bigint(this),
if_right_string(this), if_right_boolean(this);
GotoIf(IsHeapNumberMap(right_map), &if_right_heapnumber);
GotoIf(IsBigIntInstanceType(right_type), &if_right_bigint);
GotoIf(IsStringInstanceType(right_type), &if_right_string);
GotoIf(IsBooleanMap(right_map), &if_right_boolean);
Branch(IsJSReceiverInstanceType(right_type), &use_symmetry,
&if_notequal);
BIND(&if_right_heapnumber);
{ {
// Check if {rhs} is also a String. result.Bind(CallRuntime(Runtime::kBigIntEqualToNumber,
Label if_rhsisstring(this, Label::kDeferred); NoContextConstant(), left, right));
GotoIfNot(IsStringInstanceType(rhs_instance_type), &use_symmetry);
// Both {lhs} and {rhs} are of type String, just do the
// string comparison then.
result.Bind(CallBuiltin(Builtins::kStringEqual, context, lhs, rhs));
if (var_type_feedback != nullptr) {
Node* lhs_feedback = CollectFeedbackForString(lhs_instance_type);
Node* rhs_feedback = CollectFeedbackForString(rhs_instance_type);
CombineFeedback(var_type_feedback,
SmiOr(lhs_feedback, rhs_feedback));
}
Goto(&end); Goto(&end);
} }
BIND(&if_lhsisheapnumber); BIND(&if_right_bigint);
{ {
// Check if {rhs} is also a HeapNumber. result.Bind(CallRuntime(Runtime::kBigIntEqual, NoContextConstant(),
Label if_rhsisheapnumber(this), if_rhsisnotheapnumber(this); left, right));
Branch(Word32Equal(lhs_instance_type, rhs_instance_type), Goto(&end);
&if_rhsisheapnumber, &if_rhsisnotheapnumber);
BIND(&if_rhsisheapnumber);
{
// Convert {lhs} and {rhs} to floating point values, and
// perform a floating point comparison.
var_fcmp_lhs.Bind(LoadHeapNumberValue(lhs));
var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs));
if (var_type_feedback != nullptr) {
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kNumber));
}
Goto(&do_fcmp);
}
BIND(&if_rhsisnotheapnumber);
{
// The {lhs} is a Number, the {rhs} is some other HeapObject.
Label if_rhsisstring(this, Label::kDeferred), if_rhsisboolean(this);
if (var_type_feedback != nullptr) {
// The {lhs} is number and {rhs} is not Smi or HeapNumber.
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny));
}
GotoIf(IsStringInstanceType(rhs_instance_type), &if_rhsisstring);
GotoIf(IsBooleanMap(rhs_map), &if_rhsisboolean);
GotoIf(IsBigIntInstanceType(rhs_instance_type), &use_symmetry);
Branch(IsJSReceiverInstanceType(rhs_instance_type), &use_symmetry,
&if_notequal);
BIND(&if_rhsisstring);
{
// The {lhs} is a HeapNumber and the {rhs} is a String; we need
// to convert the {rhs} to a Number.
Goto(&do_rhsstringtonumber);
}
BIND(&if_rhsisboolean);
{
// The {rhs} is a Boolean, convert it to a Smi first.
var_rhs.Bind(LoadObjectField(rhs, Oddball::kToNumberOffset));
Goto(&loop);
}
}
} }
BIND(&if_lhsisbigint); BIND(&if_right_string);
{ {
if (var_type_feedback != nullptr) { result.Bind(CallRuntime(Runtime::kBigIntEqualToString,
var_type_feedback->Bind( NoContextConstant(), left, right));
SmiConstant(CompareOperationFeedback::kAny)); Goto(&end);
}
Label if_rhsisheapnumber(this), if_rhsisbigint(this),
if_rhsisstring(this), if_rhsisboolean(this);
GotoIf(IsHeapNumberMap(rhs_map), &if_rhsisheapnumber);
GotoIf(IsBigIntInstanceType(rhs_instance_type), &if_rhsisbigint);
GotoIf(IsStringInstanceType(rhs_instance_type), &if_rhsisstring);
GotoIf(IsBooleanMap(rhs_map), &if_rhsisboolean);
Branch(IsJSReceiverInstanceType(rhs_instance_type), &use_symmetry,
&if_notequal);
BIND(&if_rhsisheapnumber);
{
result.Bind(CallRuntime(Runtime::kBigIntEqualToNumber,
NoContextConstant(), lhs, rhs));
Goto(&end);
}
BIND(&if_rhsisbigint);
{
result.Bind(CallRuntime(Runtime::kBigIntEqual, NoContextConstant(),
lhs, rhs));
Goto(&end);
}
BIND(&if_rhsisstring);
{
result.Bind(CallRuntime(Runtime::kBigIntEqualToString,
NoContextConstant(), lhs, rhs));
Goto(&end);
}
BIND(&if_rhsisboolean);
{
var_rhs.Bind(LoadObjectField(rhs, Oddball::kToNumberOffset));
Goto(&loop);
}
} }
BIND(&if_lhsisoddball); BIND(&if_right_boolean);
{ {
if (var_type_feedback != nullptr) { var_right.Bind(LoadObjectField(right, Oddball::kToNumberOffset));
var_type_feedback->Bind( Goto(&loop);
SmiConstant(CompareOperationFeedback::kAny)); }
} }
// The {lhs} is an Oddball and {rhs} is some other HeapObject. BIND(&if_left_oddball);
Label if_lhsisboolean(this), if_lhsisnotboolean(this); {
Node* boolean_map = BooleanMapConstant(); if (var_type_feedback != nullptr) {
Branch(WordEqual(lhs_map, boolean_map), &if_lhsisboolean, var_type_feedback->Bind(SmiConstant(CompareOperationFeedback::kAny));
&if_lhsisnotboolean);
BIND(&if_lhsisboolean);
{
// The {lhs} is a Boolean, check if {rhs} is also a Boolean.
Label if_rhsisboolean(this), if_rhsisnotboolean(this);
Branch(WordEqual(rhs_map, boolean_map), &if_rhsisboolean,
&if_rhsisnotboolean);
BIND(&if_rhsisboolean);
{
// Both {lhs} and {rhs} are distinct Boolean values.
Goto(&if_notequal);
}
BIND(&if_rhsisnotboolean);
{
// Convert the {lhs} to a Number first.
var_lhs.Bind(LoadObjectField(lhs, Oddball::kToNumberOffset));
Goto(&loop);
}
}
BIND(&if_lhsisnotboolean);
{
// The {lhs} is either Null or Undefined; check if the {rhs} is
// undetectable (i.e. either also Null or Undefined or some
// undetectable JSReceiver).
Branch(IsUndetectableMap(rhs_map), &if_equal, &if_notequal);
}
} }
BIND(&if_lhsissymbol); Label if_left_boolean(this);
GotoIf(IsBooleanMap(left_map), &if_left_boolean);
// {left} is either Null or Undefined. Check if {right} is
// undetectable (which includes Null and Undefined).
Branch(IsUndetectableMap(right_map), &if_equal, &if_notequal);
BIND(&if_left_boolean);
{ {
// Check if the {rhs} is a JSReceiver. // If {right} is a Boolean too, it must be a different Boolean.
Label if_rhsisreceiver(this), if_rhsisnotreceiver(this); GotoIf(WordEqual(right_map, left_map), &if_notequal);
Branch(IsJSReceiverInstanceType(rhs_instance_type), &if_rhsisreceiver, // Otherwise, convert {left} to number and try again.
&if_rhsisnotreceiver); var_left.Bind(LoadObjectField(left, Oddball::kToNumberOffset));
Goto(&loop);
BIND(&if_rhsisreceiver);
{
// The {lhs} is a Primitive and the {rhs} is a JSReceiver.
// Swapping {lhs} and {rhs} is not observable and doesn't
// matter for the result, so we can just swap them and use
// the JSReceiver handling below (for {lhs} being a JSReceiver).
if (var_type_feedback != nullptr) {
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny));
}
Goto(&use_symmetry);
}
BIND(&if_rhsisnotreceiver);
{
// The {rhs} is not a JSReceiver and also not the same Symbol
// as the {lhs}, so this equality check is considered false.
if (var_type_feedback != nullptr) {
Label if_rhsissymbol(this), if_rhsisnotsymbol(this);
Branch(IsSymbolInstanceType(rhs_instance_type), &if_rhsissymbol,
&if_rhsisnotsymbol);
BIND(&if_rhsissymbol);
{
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kSymbol));
Goto(&if_notequal);
}
BIND(&if_rhsisnotsymbol);
{
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny));
Goto(&if_notequal);
}
} else {
Goto(&if_notequal);
}
}
} }
}
BIND(&if_lhsisreceiver); BIND(&if_left_symbol);
{ {
CSA_ASSERT(this, IsJSReceiverInstanceType(lhs_instance_type)); Label if_right_receiver(this);
// Check if the {rhs} is also a JSReceiver. GotoIf(IsJSReceiverInstanceType(right_type), &if_right_receiver);
Label if_rhsisreceiver(this), if_rhsisnotreceiver(this); // {right} is not a JSReceiver and also not the same Symbol as {left},
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); // so the result is "not equal".
Branch(IsJSReceiverInstanceType(rhs_instance_type), &if_rhsisreceiver, if (var_type_feedback != nullptr) {
&if_rhsisnotreceiver); Label if_right_symbol(this);
GotoIf(IsSymbolInstanceType(right_type), &if_right_symbol);
var_type_feedback->Bind(SmiConstant(CompareOperationFeedback::kAny));
Goto(&if_notequal);
BIND(&if_rhsisreceiver); BIND(&if_right_symbol);
{ {
if (var_type_feedback != nullptr) { CombineFeedback(var_type_feedback,
// The {lhs} and {rhs} are receivers. SmiConstant(CompareOperationFeedback::kSymbol));
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kReceiver));
}
// Both {lhs} and {rhs} are different JSReceiver references, so
// this cannot be considered equal.
Goto(&if_notequal); Goto(&if_notequal);
} }
} else {
Goto(&if_notequal);
}
BIND(&if_rhsisnotreceiver); BIND(&if_right_receiver);
{ {
if (var_type_feedback != nullptr) { // {left} is a Primitive and {right} is a JSReceiver, so swapping
var_type_feedback->Bind( // the order is not observable.
SmiConstant(CompareOperationFeedback::kAny)); if (var_type_feedback != nullptr) {
} var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny));
// Check if {rhs} is Null or Undefined (an undetectable check
// is sufficient here, since we already know that {rhs} is not
// a JSReceiver).
Label if_rhsisundetectable(this),
if_rhsisnotundetectable(this, Label::kDeferred);
Branch(IsUndetectableMap(rhs_map), &if_rhsisundetectable,
&if_rhsisnotundetectable);
BIND(&if_rhsisundetectable);
Branch(IsUndetectableMap(lhs_map), &if_equal, &if_notequal);
BIND(&if_rhsisnotundetectable);
{
// The {rhs} is some Primitive different from Null and
// Undefined, need to convert {lhs} to Primitive first.
Callable callable =
CodeFactory::NonPrimitiveToPrimitive(isolate());
var_lhs.Bind(CallStub(callable, context, lhs));
Goto(&loop);
}
} }
Goto(&use_symmetry);
}
}
BIND(&if_left_receiver);
{
CSA_ASSERT(this, IsJSReceiverInstanceType(left_type));
Label if_right_not_receiver(this);
GotoIfNot(IsJSReceiverInstanceType(right_type), &if_right_not_receiver);
// {left} and {right} are different JSReceiver references.
if (var_type_feedback != nullptr) {
CombineFeedback(var_type_feedback,
SmiConstant(CompareOperationFeedback::kReceiver));
}
Goto(&if_notequal);
BIND(&if_right_not_receiver);
{
if (var_type_feedback != nullptr) {
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny));
}
Label if_right_null_or_undefined(this);
GotoIf(IsUndetectableMap(right_map), &if_right_null_or_undefined);
// {right} is a Primitive; convert {left} to Primitive too.
Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate());
var_left.Bind(CallStub(callable, context, left));
Goto(&loop);
BIND(&if_right_null_or_undefined);
Branch(IsUndetectableMap(left_map), &if_equal, &if_notequal);
} }
} }
} }
BIND(&do_rhsstringtonumber); BIND(&do_right_stringtonumber);
{ {
var_rhs.Bind(CallBuiltin(Builtins::kStringToNumber, context, rhs)); var_right.Bind(CallBuiltin(Builtins::kStringToNumber, context, right));
Goto(&loop);
}
BIND(&use_symmetry);
{
var_left.Bind(right);
var_right.Bind(left);
Goto(&loop); Goto(&loop);
} }
} }
BIND(&use_symmetry); BIND(&do_float_comparison);
{ {
var_lhs.Bind(rhs); Branch(Float64Equal(var_left_float, var_right_float), &if_equal,
var_rhs.Bind(lhs); &if_notequal);
Goto(&loop);
}
BIND(&do_fcmp);
{
// Load the {lhs} and {rhs} floating point values.
Node* lhs = var_fcmp_lhs.value();
Node* rhs = var_fcmp_rhs.value();
// Perform a fast floating point comparison.
Branch(Float64Equal(lhs, rhs), &if_equal, &if_notequal);
} }
BIND(&if_equal); BIND(&if_equal);