Commit 8a809501 authored by bmeurer's avatar bmeurer Committed by Commit bot

[compiler] Introduce code stubs for abstract equality.

Add EqualStub and NotEqualStub, based on the CodeStubAssembler, and hook
them up with TurboFan and Ignition. The stubs are a full implementation
of abstract equality for ES6 plus the current SIMD.js draft, unlike the
generic version of the CompareIC, which only implements a subset with
funky runtime fallbacks.

Drive-by-fix: Introduce some common helper methods.

R=epertoso@chromium.org
BUG=chromium:592690
LOG=n

Review URL: https://codereview.chromium.org/1795793002

Cr-Commit-Position: refs/heads/master@{#34806}
parent 2d9c29cc
......@@ -218,6 +218,18 @@ Callable CodeFactory::GreaterThanOrEqual(Isolate* isolate) {
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::Equal(Isolate* isolate) {
EqualStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::NotEqual(Isolate* isolate) {
NotEqualStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::StrictEqual(Isolate* isolate) {
StrictEqualStub stub(isolate);
......
......@@ -79,6 +79,8 @@ class CodeFactory final {
static Callable LessThanOrEqual(Isolate* isolate);
static Callable GreaterThan(Isolate* isolate);
static Callable GreaterThanOrEqual(Isolate* isolate);
static Callable Equal(Isolate* isolate);
static Callable NotEqual(Isolate* isolate);
static Callable StrictEqual(Isolate* isolate);
static Callable StrictNotEqual(Isolate* isolate);
......
......@@ -575,7 +575,7 @@ void GenerateAbstractRelationalComparison(
assembler->Bind(&if_rhsisnotsmi);
{
// Load the map of {rhs}.
Node* rhs_map = assembler->LoadObjectField(rhs, HeapObject::kMapOffset);
Node* rhs_map = assembler->LoadMap(rhs);
// Check if the {rhs} is a HeapNumber.
Node* number_map = assembler->HeapNumberMapConstant();
......@@ -612,7 +612,7 @@ void GenerateAbstractRelationalComparison(
Node* number_map = assembler->HeapNumberMapConstant();
// Load the map of {lhs}.
Node* lhs_map = assembler->LoadObjectField(lhs, HeapObject::kMapOffset);
Node* lhs_map = assembler->LoadMap(lhs);
// Check if {rhs} is a Smi or a HeapObject.
Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
......@@ -651,7 +651,7 @@ void GenerateAbstractRelationalComparison(
assembler->Bind(&if_rhsisnotsmi);
{
// Load the map of {rhs}.
Node* rhs_map = assembler->LoadObjectField(rhs, HeapObject::kMapOffset);
Node* rhs_map = assembler->LoadMap(rhs);
// Check if {lhs} is a HeapNumber.
Label if_lhsisnumber(assembler), if_lhsisnotnumber(assembler);
......@@ -848,6 +848,658 @@ void GenerateAbstractRelationalComparison(
enum ResultMode { kDontNegateResult, kNegateResult };
void GenerateEqual_Same(compiler::CodeStubAssembler* assembler,
compiler::Node* value,
compiler::CodeStubAssembler::Label* if_equal,
compiler::CodeStubAssembler::Label* if_notequal) {
// In case of abstract or strict equality checks, we need additional checks
// for NaN values because they are not considered equal, even if both the
// left and the right hand side reference exactly the same value.
// TODO(bmeurer): This seems to violate the SIMD.js specification, but it
// seems to be what is tested in the current SIMD.js testsuite.
typedef compiler::CodeStubAssembler::Label Label;
typedef compiler::Node Node;
// Check if {value} is a Smi or a HeapObject.
Label if_valueissmi(assembler), if_valueisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(value), &if_valueissmi,
&if_valueisnotsmi);
assembler->Bind(&if_valueisnotsmi);
{
// Load the map of {value}.
Node* value_map = assembler->LoadMap(value);
// Check if {value} (and therefore {rhs}) is a HeapNumber.
Node* number_map = assembler->HeapNumberMapConstant();
Label if_valueisnumber(assembler), if_valueisnotnumber(assembler);
assembler->Branch(assembler->WordEqual(value_map, number_map),
&if_valueisnumber, &if_valueisnotnumber);
assembler->Bind(&if_valueisnumber);
{
// Convert {value} (and therefore {rhs}) to floating point value.
Node* value_value = assembler->LoadHeapNumberValue(value);
// Check if the HeapNumber value is a NaN.
assembler->BranchIfFloat64IsNaN(value_value, if_notequal, if_equal);
}
assembler->Bind(&if_valueisnotnumber);
assembler->Goto(if_equal);
}
assembler->Bind(&if_valueissmi);
assembler->Goto(if_equal);
}
void GenerateEqual_Simd128Value_HeapObject(
compiler::CodeStubAssembler* assembler, compiler::Node* lhs,
compiler::Node* lhs_map, compiler::Node* rhs, compiler::Node* rhs_map,
compiler::CodeStubAssembler::Label* if_equal,
compiler::CodeStubAssembler::Label* if_notequal) {
typedef compiler::CodeStubAssembler::Label Label;
typedef compiler::Node Node;
// Check if {lhs} and {rhs} have the same map.
Label if_mapsame(assembler), if_mapnotsame(assembler);
assembler->Branch(assembler->WordEqual(lhs_map, rhs_map), &if_mapsame,
&if_mapnotsame);
assembler->Bind(&if_mapsame);
{
// Both {lhs} and {rhs} are Simd128Values with the same map, need special
// handling for Float32x4 because of NaN comparisons.
Label if_float32x4(assembler), if_notfloat32x4(assembler);
Node* float32x4_map =
assembler->HeapConstant(assembler->factory()->float32x4_map());
assembler->Branch(assembler->WordEqual(lhs_map, float32x4_map),
&if_float32x4, &if_notfloat32x4);
assembler->Bind(&if_float32x4);
{
// Both {lhs} and {rhs} are Float32x4, compare the lanes individually
// using a floating point comparison.
for (int offset = Float32x4::kValueOffset - kHeapObjectTag;
offset < Float32x4::kSize - kHeapObjectTag;
offset += sizeof(float)) {
// Load the floating point values for {lhs} and {rhs}.
Node* lhs_value = assembler->Load(MachineType::Float32(), lhs,
assembler->IntPtrConstant(offset));
Node* rhs_value = assembler->Load(MachineType::Float32(), rhs,
assembler->IntPtrConstant(offset));
// Perform a floating point comparison.
Label if_valueequal(assembler), if_valuenotequal(assembler);
assembler->Branch(assembler->Float32Equal(lhs_value, rhs_value),
&if_valueequal, &if_valuenotequal);
assembler->Bind(&if_valuenotequal);
assembler->Goto(if_notequal);
assembler->Bind(&if_valueequal);
}
// All 4 lanes match, {lhs} and {rhs} considered equal.
assembler->Goto(if_equal);
}
assembler->Bind(&if_notfloat32x4);
{
// For other Simd128Values we just perform a bitwise comparison.
for (int offset = Simd128Value::kValueOffset - kHeapObjectTag;
offset < Simd128Value::kSize - kHeapObjectTag;
offset += kPointerSize) {
// Load the word values for {lhs} and {rhs}.
Node* lhs_value = assembler->Load(MachineType::Pointer(), lhs,
assembler->IntPtrConstant(offset));
Node* rhs_value = assembler->Load(MachineType::Pointer(), rhs,
assembler->IntPtrConstant(offset));
// Perform a bitwise word-comparison.
Label if_valueequal(assembler), if_valuenotequal(assembler);
assembler->Branch(assembler->WordEqual(lhs_value, rhs_value),
&if_valueequal, &if_valuenotequal);
assembler->Bind(&if_valuenotequal);
assembler->Goto(if_notequal);
assembler->Bind(&if_valueequal);
}
// Bitwise comparison succeeded, {lhs} and {rhs} considered equal.
assembler->Goto(if_equal);
}
}
assembler->Bind(&if_mapnotsame);
assembler->Goto(if_notequal);
}
// ES6 section 7.2.12 Abstract Equality Comparison
void GenerateEqual(compiler::CodeStubAssembler* assembler, ResultMode mode) {
// This is a slightly optimized version of Object::Equals represented as
// scheduled TurboFan graph utilizing the CodeStubAssembler. Whenever you
// change something functionality wise in here, remember to update the
// Object::Equals method as well.
typedef compiler::CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef compiler::CodeStubAssembler::Variable Variable;
Node* context = assembler->Parameter(2);
Label if_equal(assembler), if_notequal(assembler);
// Shared entry for floating point comparison.
Label do_fcmp(assembler);
Variable var_fcmp_lhs(assembler, MachineRepresentation::kFloat64),
var_fcmp_rhs(assembler, MachineRepresentation::kFloat64);
// We might need to loop several times due to ToPrimitive and/or ToNumber
// conversions.
Variable var_lhs(assembler, MachineRepresentation::kTagged),
var_rhs(assembler, MachineRepresentation::kTagged);
Variable* loop_vars[2] = {&var_lhs, &var_rhs};
Label loop(assembler, 2, loop_vars);
var_lhs.Bind(assembler->Parameter(0));
var_rhs.Bind(assembler->Parameter(1));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {lhs} and {rhs} values.
Node* lhs = var_lhs.value();
Node* rhs = var_rhs.value();
// Check if {lhs} and {rhs} refer to the same object.
Label if_same(assembler), if_notsame(assembler);
assembler->Branch(assembler->WordEqual(lhs, rhs), &if_same, &if_notsame);
assembler->Bind(&if_same);
{
// The {lhs} and {rhs} reference the exact same value, yet we need special
// treatment for HeapNumber, as NaN is not equal to NaN.
GenerateEqual_Same(assembler, lhs, &if_equal, &if_notequal);
}
assembler->Bind(&if_notsame);
{
// Check if {lhs} is a Smi or a HeapObject.
Label if_lhsissmi(assembler), if_lhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(lhs), &if_lhsissmi,
&if_lhsisnotsmi);
assembler->Bind(&if_lhsissmi);
{
// Check if {rhs} is a Smi or a HeapObject.
Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi,
&if_rhsisnotsmi);
assembler->Bind(&if_rhsissmi);
assembler->Goto(&if_notequal);
assembler->Bind(&if_rhsisnotsmi);
{
// Load the map of {rhs}.
Node* rhs_map = assembler->LoadMap(rhs);
// Check if {rhs} is a HeapNumber.
Node* number_map = assembler->HeapNumberMapConstant();
Label if_rhsisnumber(assembler),
if_rhsisnotnumber(assembler, Label::kDeferred);
assembler->Branch(assembler->WordEqual(rhs_map, number_map),
&if_rhsisnumber, &if_rhsisnotnumber);
assembler->Bind(&if_rhsisnumber);
{
// Convert {lhs} and {rhs} to floating point values, and
// perform a floating point comparison.
var_fcmp_lhs.Bind(assembler->SmiToFloat64(lhs));
var_fcmp_rhs.Bind(assembler->LoadHeapNumberValue(rhs));
assembler->Goto(&do_fcmp);
}
assembler->Bind(&if_rhsisnotnumber);
{
// Load the instance type of the {rhs}.
Node* rhs_instance_type = assembler->LoadMapInstanceType(rhs_map);
// Check if the {rhs} is a String.
Label if_rhsisstring(assembler, Label::kDeferred),
if_rhsisnotstring(assembler, Label::kDeferred);
assembler->Branch(assembler->Int32LessThan(
rhs_instance_type, assembler->Int32Constant(
FIRST_NONSTRING_TYPE)),
&if_rhsisstring, &if_rhsisnotstring);
assembler->Bind(&if_rhsisstring);
{
// Convert the {rhs} to a Number.
Callable callable = CodeFactory::ToNumber(assembler->isolate());
var_rhs.Bind(assembler->CallStub(callable, context, rhs));
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotstring);
{
// Check if the {rhs} is a Boolean.
Node* boolean_map = assembler->BooleanMapConstant();
Label if_rhsisboolean(assembler, Label::kDeferred),
if_rhsisnotboolean(assembler, Label::kDeferred);
assembler->Branch(assembler->WordEqual(rhs_map, boolean_map),
&if_rhsisboolean, &if_rhsisnotboolean);
assembler->Bind(&if_rhsisboolean);
{
// The {rhs} is a Boolean, load its number value.
var_rhs.Bind(
assembler->LoadObjectField(rhs, Oddball::kToNumberOffset));
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotboolean);
{
// Check if the {rhs} is a Receiver.
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
Label if_rhsisreceiver(assembler, Label::kDeferred),
if_rhsisnotreceiver(assembler, Label::kDeferred);
assembler->Branch(
assembler->Int32LessThanOrEqual(
assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE),
rhs_instance_type),
&if_rhsisreceiver, &if_rhsisnotreceiver);
assembler->Bind(&if_rhsisreceiver);
{
// Convert {rhs} to a primitive first (passing no hint).
// TODO(bmeurer): Hook up ToPrimitiveStub here once it exists.
var_rhs.Bind(assembler->CallRuntime(Runtime::kToPrimitive,
context, rhs));
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotreceiver);
assembler->Goto(&if_notequal);
}
}
}
}
}
assembler->Bind(&if_lhsisnotsmi);
{
// Check if {rhs} is a Smi or a HeapObject.
Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi,
&if_rhsisnotsmi);
assembler->Bind(&if_rhsissmi);
{
// The {lhs} is a HeapObject and the {rhs} is a Smi; swapping {lhs}
// and {rhs} is not observable and doesn't matter for the result, so
// we can just swap them and use the Smi handling above (for {lhs}
// being a Smi).
var_lhs.Bind(rhs);
var_rhs.Bind(lhs);
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotsmi);
{
Label if_lhsisstring(assembler), if_lhsisnumber(assembler),
if_lhsissymbol(assembler), if_lhsissimd128value(assembler),
if_lhsisoddball(assembler), if_lhsisreceiver(assembler);
// Both {lhs} and {rhs} are HeapObjects, load their maps
// and their instance types.
Node* lhs_map = assembler->LoadMap(lhs);
Node* rhs_map = assembler->LoadMap(rhs);
// Load the instance types of {lhs} and {rhs}.
Node* lhs_instance_type = assembler->LoadMapInstanceType(lhs_map);
Node* rhs_instance_type = assembler->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(assembler);
case_values[i] = i;
}
case_labels[FIRST_NONSTRING_TYPE + 0] = &if_lhsisnumber;
case_values[FIRST_NONSTRING_TYPE + 0] = HEAP_NUMBER_TYPE;
case_labels[FIRST_NONSTRING_TYPE + 1] = &if_lhsissymbol;
case_values[FIRST_NONSTRING_TYPE + 1] = SYMBOL_TYPE;
case_labels[FIRST_NONSTRING_TYPE + 2] = &if_lhsissimd128value;
case_values[FIRST_NONSTRING_TYPE + 2] = SIMD128_VALUE_TYPE;
case_labels[FIRST_NONSTRING_TYPE + 3] = &if_lhsisoddball;
case_values[FIRST_NONSTRING_TYPE + 3] = ODDBALL_TYPE;
assembler->Switch(lhs_instance_type, &if_lhsisreceiver, case_values,
case_labels, arraysize(case_values));
for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) {
assembler->Bind(case_labels[i]);
assembler->Goto(&if_lhsisstring);
delete case_labels[i];
}
assembler->Bind(&if_lhsisstring);
{
// Check if {rhs} is also a String.
Label if_rhsisstring(assembler),
if_rhsisnotstring(assembler, Label::kDeferred);
assembler->Branch(assembler->Int32LessThan(
rhs_instance_type, assembler->Int32Constant(
FIRST_NONSTRING_TYPE)),
&if_rhsisstring, &if_rhsisnotstring);
assembler->Bind(&if_rhsisstring);
{
// Both {lhs} and {rhs} are of type String, just do the
// string comparison then.
Callable callable =
(mode == kDontNegateResult)
? CodeFactory::StringEqual(assembler->isolate())
: CodeFactory::StringNotEqual(assembler->isolate());
assembler->TailCallStub(callable, context, lhs, rhs);
}
assembler->Bind(&if_rhsisnotstring);
{
// The {lhs} is a String and the {rhs} is some other HeapObject.
// Swapping {lhs} and {rhs} is not observable and doesn't matter
// for the result, so we can just swap them and use the String
// handling below (for {rhs} being a String).
var_lhs.Bind(rhs);
var_rhs.Bind(lhs);
assembler->Goto(&loop);
}
}
assembler->Bind(&if_lhsisnumber);
{
// Check if {rhs} is also a HeapNumber.
Label if_rhsisnumber(assembler),
if_rhsisnotnumber(assembler, Label::kDeferred);
assembler->Branch(
assembler->Word32Equal(lhs_instance_type, rhs_instance_type),
&if_rhsisnumber, &if_rhsisnotnumber);
assembler->Bind(&if_rhsisnumber);
{
// Convert {lhs} and {rhs} to floating point values, and
// perform a floating point comparison.
var_fcmp_lhs.Bind(assembler->LoadHeapNumberValue(lhs));
var_fcmp_rhs.Bind(assembler->LoadHeapNumberValue(rhs));
assembler->Goto(&do_fcmp);
}
assembler->Bind(&if_rhsisnotnumber);
{
// The {lhs} is a Number, the {rhs} is some other HeapObject.
Label if_rhsisstring(assembler, Label::kDeferred),
if_rhsisnotstring(assembler);
assembler->Branch(
assembler->Int32LessThan(
rhs_instance_type,
assembler->Int32Constant(FIRST_NONSTRING_TYPE)),
&if_rhsisstring, &if_rhsisnotstring);
assembler->Bind(&if_rhsisstring);
{
// The {rhs} is a String and the {lhs} is a HeapNumber; we need
// to convert the {rhs} to a Number and compare the output to
// the Number on the {lhs}.
Callable callable = CodeFactory::ToNumber(assembler->isolate());
var_rhs.Bind(assembler->CallStub(callable, context, rhs));
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotstring);
{
// Check if the {rhs} is a JSReceiver.
Label if_rhsisreceiver(assembler, Label::kDeferred),
if_rhsisnotreceiver(assembler);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
assembler->Branch(
assembler->Int32LessThanOrEqual(
assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE),
rhs_instance_type),
&if_rhsisreceiver, &if_rhsisnotreceiver);
assembler->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).
var_lhs.Bind(rhs);
var_rhs.Bind(lhs);
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotreceiver);
{
// Check if {rhs} is a Boolean.
Label if_rhsisboolean(assembler),
if_rhsisnotboolean(assembler);
Node* boolean_map = assembler->BooleanMapConstant();
assembler->Branch(assembler->WordEqual(rhs_map, boolean_map),
&if_rhsisboolean, &if_rhsisnotboolean);
assembler->Bind(&if_rhsisboolean);
{
// The {rhs} is a Boolean, convert it to a Smi first.
var_rhs.Bind(assembler->LoadObjectField(
rhs, Oddball::kToNumberOffset));
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotboolean);
assembler->Goto(&if_notequal);
}
}
}
}
assembler->Bind(&if_lhsisoddball);
{
// The {lhs} is an Oddball and {rhs} is some other HeapObject.
Label if_lhsisboolean(assembler), if_lhsisnotboolean(assembler);
Node* boolean_map = assembler->BooleanMapConstant();
assembler->Branch(assembler->WordEqual(lhs_map, boolean_map),
&if_lhsisboolean, &if_lhsisnotboolean);
assembler->Bind(&if_lhsisboolean);
{
// The {lhs} is a Boolean, check if {rhs} is also a Boolean.
Label if_rhsisboolean(assembler), if_rhsisnotboolean(assembler);
assembler->Branch(assembler->WordEqual(rhs_map, boolean_map),
&if_rhsisboolean, &if_rhsisnotboolean);
assembler->Bind(&if_rhsisboolean);
{
// Both {lhs} and {rhs} are distinct Boolean values.
assembler->Goto(&if_notequal);
}
assembler->Bind(&if_rhsisnotboolean);
{
// Convert the {lhs} to a Number first.
var_lhs.Bind(
assembler->LoadObjectField(lhs, Oddball::kToNumberOffset));
assembler->Goto(&loop);
}
}
assembler->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).
Node* rhs_bitfield = assembler->LoadMapBitField(rhs_map);
assembler->BranchIfWord32Equal(
assembler->Word32And(
rhs_bitfield,
assembler->Int32Constant(1 << Map::kIsUndetectable)),
assembler->Int32Constant(0), &if_notequal, &if_equal);
}
}
assembler->Bind(&if_lhsissymbol);
{
// Check if the {rhs} is a JSReceiver.
Label if_rhsisreceiver(assembler, Label::kDeferred),
if_rhsisnotreceiver(assembler);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
assembler->Branch(
assembler->Int32LessThanOrEqual(
assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE),
rhs_instance_type),
&if_rhsisreceiver, &if_rhsisnotreceiver);
assembler->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).
var_lhs.Bind(rhs);
var_rhs.Bind(lhs);
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotreceiver);
{
// The {rhs} is not a JSReceiver and also not the same Symbol
// as the {lhs}, so this is equality check is considered false.
assembler->Goto(&if_notequal);
}
}
assembler->Bind(&if_lhsissimd128value);
{
// Check if the {rhs} is also a Simd128Value.
Label if_rhsissimd128value(assembler),
if_rhsisnotsimd128value(assembler);
assembler->Branch(
assembler->Word32Equal(lhs_instance_type, rhs_instance_type),
&if_rhsissimd128value, &if_rhsisnotsimd128value);
assembler->Bind(&if_rhsissimd128value);
{
// Both {lhs} and {rhs} is a Simd128Value.
GenerateEqual_Simd128Value_HeapObject(assembler, lhs, lhs_map,
rhs, rhs_map, &if_equal,
&if_notequal);
}
assembler->Bind(&if_rhsisnotsimd128value);
{
// Check if the {rhs} is a JSReceiver.
Label if_rhsisreceiver(assembler, Label::kDeferred),
if_rhsisnotreceiver(assembler);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
assembler->Branch(
assembler->Int32LessThanOrEqual(
assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE),
rhs_instance_type),
&if_rhsisreceiver, &if_rhsisnotreceiver);
assembler->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).
var_lhs.Bind(rhs);
var_rhs.Bind(lhs);
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotreceiver);
{
// The {rhs} is some other Primitive.
assembler->Goto(&if_notequal);
}
}
}
assembler->Bind(&if_lhsisreceiver);
{
// Check if the {rhs} is also a JSReceiver.
Label if_rhsisreceiver(assembler), if_rhsisnotreceiver(assembler);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
assembler->Branch(
assembler->Int32LessThanOrEqual(
assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE),
rhs_instance_type),
&if_rhsisreceiver, &if_rhsisnotreceiver);
assembler->Bind(&if_rhsisreceiver);
{
// Both {lhs} and {rhs} are different JSReceiver references, so
// this cannot be considered equal.
assembler->Goto(&if_notequal);
}
assembler->Bind(&if_rhsisnotreceiver);
{
// 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(assembler),
if_rhsisnotundetectable(assembler, Label::kDeferred);
Node* rhs_bitfield = assembler->LoadMapBitField(rhs_map);
assembler->BranchIfWord32Equal(
assembler->Word32And(
rhs_bitfield,
assembler->Int32Constant(1 << Map::kIsUndetectable)),
assembler->Int32Constant(0), &if_rhsisnotundetectable,
&if_rhsisundetectable);
assembler->Bind(&if_rhsisundetectable);
{
// Check if {lhs} is an undetectable JSReceiver.
Node* lhs_bitfield = assembler->LoadMapBitField(lhs_map);
assembler->BranchIfWord32Equal(
assembler->Word32And(
lhs_bitfield,
assembler->Int32Constant(1 << Map::kIsUndetectable)),
assembler->Int32Constant(0), &if_notequal, &if_equal);
}
assembler->Bind(&if_rhsisnotundetectable);
{
// The {rhs} is some Primitive different from Null and
// Undefined, need to convert {lhs} to Primitive first.
// TODO(bmeurer): Hook up ToPrimitiveStub here once it exists.
var_lhs.Bind(assembler->CallRuntime(Runtime::kToPrimitive,
context, lhs));
assembler->Goto(&loop);
}
}
}
}
}
}
}
assembler->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.
assembler->BranchIfFloat64Equal(lhs, rhs, &if_equal, &if_notequal);
}
assembler->Bind(&if_equal);
assembler->Return(assembler->BooleanConstant(mode == kDontNegateResult));
assembler->Bind(&if_notequal);
assembler->Return(assembler->BooleanConstant(mode == kNegateResult));
}
void GenerateStrictEqual(compiler::CodeStubAssembler* assembler,
ResultMode mode) {
// Here's pseudo-code for the algorithm below in case of kDontNegateResult
......@@ -915,39 +1567,7 @@ void GenerateStrictEqual(compiler::CodeStubAssembler* assembler,
{
// The {lhs} and {rhs} reference the exact same value, yet we need special
// treatment for HeapNumber, as NaN is not equal to NaN.
// TODO(bmeurer): This seems to violate the SIMD.js specification, but it
// seems to be what is tested in the current SIMD.js testsuite.
// Check if {lhs} (and therefore {rhs}) is a Smi or a HeapObject.
Label if_lhsissmi(assembler), if_lhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi);
assembler->Bind(&if_lhsisnotsmi);
{
// Load the map of {lhs}.
Node* lhs_map = assembler->LoadObjectField(lhs, HeapObject::kMapOffset);
// Check if {lhs} (and therefore {rhs}) is a HeapNumber.
Node* number_map = assembler->HeapNumberMapConstant();
Label if_lhsisnumber(assembler), if_lhsisnotnumber(assembler);
assembler->Branch(assembler->WordEqual(lhs_map, number_map),
&if_lhsisnumber, &if_lhsisnotnumber);
assembler->Bind(&if_lhsisnumber);
{
// Convert {lhs} (and therefore {rhs}) to floating point value.
Node* lhs_value = assembler->LoadHeapNumberValue(lhs);
// Check if the HeapNumber value is a NaN.
assembler->BranchIfFloat64IsNaN(lhs_value, &if_notequal, &if_equal);
}
assembler->Bind(&if_lhsisnotnumber);
assembler->Goto(&if_equal);
}
assembler->Bind(&if_lhsissmi);
assembler->Goto(&if_equal);
GenerateEqual_Same(assembler, lhs, &if_equal, &if_notequal);
}
assembler->Bind(&if_notsame);
......@@ -963,7 +1583,7 @@ void GenerateStrictEqual(compiler::CodeStubAssembler* assembler,
assembler->Bind(&if_lhsisnotsmi);
{
// Load the map of {lhs}.
Node* lhs_map = assembler->LoadObjectField(lhs, HeapObject::kMapOffset);
Node* lhs_map = assembler->LoadMap(lhs);
// Check if {lhs} is a HeapNumber.
Label if_lhsisnumber(assembler), if_lhsisnotnumber(assembler);
......@@ -991,8 +1611,7 @@ void GenerateStrictEqual(compiler::CodeStubAssembler* assembler,
assembler->Bind(&if_rhsisnotsmi);
{
// Load the map of {rhs}.
Node* rhs_map =
assembler->LoadObjectField(rhs, HeapObject::kMapOffset);
Node* rhs_map = assembler->LoadMap(rhs);
// Check if {rhs} is also a HeapNumber.
Label if_rhsisnumber(assembler), if_rhsisnotnumber(assembler);
......@@ -1074,11 +1693,13 @@ void GenerateStrictEqual(compiler::CodeStubAssembler* assembler,
assembler->Bind(&if_lhsissimd128value);
{
// TODO(bmeurer): Inline the Simd128Value equality check.
Runtime::FunctionId function_id = (mode == kDontNegateResult)
? Runtime::kStrictEqual
: Runtime::kStrictNotEqual;
assembler->TailCallRuntime(function_id, context, lhs, rhs);
// Load the map of {rhs}.
Node* rhs_map = assembler->LoadMap(rhs);
// Check if {rhs} is also a Simd128Value that is equal to {lhs}.
GenerateEqual_Simd128Value_HeapObject(assembler, lhs, lhs_map,
rhs, rhs_map, &if_equal,
&if_notequal);
}
assembler->Bind(&if_lhsisnotsimd128value);
......@@ -1105,7 +1726,7 @@ void GenerateStrictEqual(compiler::CodeStubAssembler* assembler,
assembler->Bind(&if_rhsisnotsmi);
{
// Load the map of the {rhs}.
Node* rhs_map = assembler->LoadObjectField(rhs, HeapObject::kMapOffset);
Node* rhs_map = assembler->LoadMap(rhs);
// The {rhs} could be a HeapNumber with the same value as {lhs}.
Label if_rhsisnumber(assembler), if_rhsisnotnumber(assembler);
......@@ -1518,6 +2139,15 @@ void GreaterThanOrEqualStub::GenerateAssembly(
GenerateAbstractRelationalComparison(assembler, kGreaterThanOrEqual);
}
void EqualStub::GenerateAssembly(compiler::CodeStubAssembler* assembler) const {
GenerateEqual(assembler, kDontNegateResult);
}
void NotEqualStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateEqual(assembler, kNegateResult);
}
void StrictEqualStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateStrictEqual(assembler, kDontNegateResult);
......@@ -1590,10 +2220,10 @@ void ToBooleanStub::GenerateAssembly(
if_valueisoddball(assembler), if_valueisother(assembler);
// The {value} is a HeapObject, load its map.
Node* value_map = assembler->LoadObjectField(value, HeapObject::kMapOffset);
Node* value_map = assembler->LoadMap(value);
// Load the {value}s instance type.
Node* value_instancetype = assembler->Load(
Node* value_instance_type = assembler->Load(
MachineType::Uint8(), value_map,
assembler->IntPtrConstant(Map::kInstanceTypeOffset - kHeapObjectTag));
......@@ -1610,7 +2240,7 @@ void ToBooleanStub::GenerateAssembly(
case_values[FIRST_NONSTRING_TYPE + 0] = HEAP_NUMBER_TYPE;
case_labels[FIRST_NONSTRING_TYPE + 1] = &if_valueisoddball;
case_values[FIRST_NONSTRING_TYPE + 1] = ODDBALL_TYPE;
assembler->Switch(value_instancetype, &if_valueisother, case_values,
assembler->Switch(value_instance_type, &if_valueisother, case_values,
case_labels, arraysize(case_values));
for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) {
assembler->Bind(case_labels[i]);
......
......@@ -103,6 +103,8 @@ namespace internal {
V(LessThanOrEqual) \
V(GreaterThan) \
V(GreaterThanOrEqual) \
V(Equal) \
V(NotEqual) \
V(StrictEqual) \
V(StrictNotEqual) \
V(StringEqual) \
......@@ -684,6 +686,22 @@ class GreaterThanOrEqualStub final : public TurboFanCodeStub {
DEFINE_TURBOFAN_CODE_STUB(GreaterThanOrEqual, TurboFanCodeStub);
};
class EqualStub final : public TurboFanCodeStub {
public:
explicit EqualStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_TURBOFAN_CODE_STUB(Equal, TurboFanCodeStub);
};
class NotEqualStub final : public TurboFanCodeStub {
public:
explicit NotEqualStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_TURBOFAN_CODE_STUB(NotEqual, TurboFanCodeStub);
};
class StrictEqualStub final : public TurboFanCodeStub {
public:
explicit StrictEqualStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
......
......@@ -105,6 +105,10 @@ Node* CodeStubAssembler::Float64Constant(double value) {
return raw_assembler_->Float64Constant(value);
}
Node* CodeStubAssembler::BooleanMapConstant() {
return HeapConstant(isolate()->factory()->boolean_map());
}
Node* CodeStubAssembler::HeapNumberMapConstant() {
return HeapConstant(isolate()->factory()->heap_number_map());
}
......@@ -222,6 +226,11 @@ Node* CodeStubAssembler::LoadHeapNumberValue(Node* object) {
IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag));
}
Node* CodeStubAssembler::LoadMapBitField(Node* map) {
return Load(MachineType::Uint8(), map,
IntPtrConstant(Map::kBitFieldOffset - kHeapObjectTag));
}
Node* CodeStubAssembler::LoadMapInstanceType(Node* map) {
return Load(MachineType::Uint8(), map,
IntPtrConstant(Map::kInstanceTypeOffset - kHeapObjectTag));
......@@ -429,8 +438,12 @@ Node* CodeStubAssembler::Projection(int index, Node* value) {
return raw_assembler_->Projection(index, value);
}
Node* CodeStubAssembler::LoadMap(Node* object) {
return LoadObjectField(object, HeapObject::kMapOffset);
}
Node* CodeStubAssembler::LoadInstanceType(Node* object) {
return LoadMapInstanceType(LoadObjectField(object, HeapObject::kMapOffset));
return LoadMapInstanceType(LoadMap(object));
}
Node* CodeStubAssembler::BitFieldDecode(Node* word32, uint32_t shift,
......@@ -527,6 +540,16 @@ void CodeStubAssembler::BranchIfFloat64GreaterThanOrEqual(Node* a, Node* b,
Goto(if_false);
}
void CodeStubAssembler::BranchIfWord32Equal(Node* a, Node* b, Label* if_true,
Label* if_false) {
Label if_equal(this), if_notequal(this);
Branch(Word32Equal(a, b), &if_equal, &if_notequal);
Bind(&if_equal);
Goto(if_true);
Bind(&if_notequal);
Goto(if_false);
}
Node* CodeStubAssembler::CallN(CallDescriptor* descriptor, Node* code_target,
Node** args) {
CallPrologue();
......
......@@ -36,6 +36,11 @@ class RawMachineLabel;
class Schedule;
#define CODE_STUB_ASSEMBLER_BINARY_OP_LIST(V) \
V(Float32Equal) \
V(Float32LessThan) \
V(Float32LessThanOrEqual) \
V(Float32GreaterThan) \
V(Float32GreaterThanOrEqual) \
V(Float64Equal) \
V(Float64LessThan) \
V(Float64LessThanOrEqual) \
......@@ -141,6 +146,7 @@ class CodeStubAssembler {
Node* BooleanConstant(bool value);
Node* ExternalConstant(ExternalReference address);
Node* Float64Constant(double value);
Node* BooleanMapConstant();
Node* HeapNumberMapConstant();
Node* Parameter(int value);
......@@ -267,6 +273,8 @@ class CodeStubAssembler {
Node* LoadObjectField(Node* object, int offset);
// Load the floating point value of a HeapNumber.
Node* LoadHeapNumberValue(Node* object);
// Load the bit field of a Map.
Node* LoadMapBitField(Node* map);
// Load the instance type of a Map.
Node* LoadMapInstanceType(Node* map);
......@@ -281,6 +289,9 @@ class CodeStubAssembler {
// Store an array element to a FixedArray.
Node* StoreFixedArrayElementNoWriteBarrier(Node* object, Node* index,
Node* value);
// Load the Map of an HeapObject.
Node* LoadMap(Node* object);
// Load the instance type of an HeapObject.
Node* LoadInstanceType(Node* object);
// Returns a node that is true if the given bit is set in |word32|.
......@@ -309,6 +320,7 @@ class CodeStubAssembler {
void BranchIfFloat64IsNaN(Node* value, Label* if_true, Label* if_false) {
BranchIfFloat64Equal(value, value, if_false, if_true);
}
void BranchIfWord32Equal(Node* a, Node* b, Label* if_true, Label* if_false);
// Helpers which delegate to RawMachineAssembler.
Factory* factory() const;
......
......@@ -87,8 +87,6 @@ REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD)
void JSGenericLowering::Lower##op(Node* node) { \
ReplaceWithRuntimeCall(node, fun); \
}
REPLACE_RUNTIME_CALL(JSEqual, Runtime::kEqual)
REPLACE_RUNTIME_CALL(JSNotEqual, Runtime::kNotEqual)
REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
REPLACE_RUNTIME_CALL(JSConvertReceiver, Runtime::kConvertReceiver)
......@@ -104,6 +102,8 @@ REPLACE_STUB_CALL(LessThan)
REPLACE_STUB_CALL(LessThanOrEqual)
REPLACE_STUB_CALL(GreaterThan)
REPLACE_STUB_CALL(GreaterThanOrEqual)
REPLACE_STUB_CALL(Equal)
REPLACE_STUB_CALL(NotEqual)
REPLACE_STUB_CALL(StrictEqual)
REPLACE_STUB_CALL(StrictNotEqual)
#undef REPLACE_STUB_CALL
......
......@@ -1176,7 +1176,7 @@ void Interpreter::DoNewWide(InterpreterAssembler* assembler) {
//
// Test if the value in the <src> register equals the accumulator.
void Interpreter::DoTestEqual(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kEqual, assembler);
DoBinaryOp(CodeFactory::Equal(isolate_), assembler);
}
......@@ -1184,7 +1184,7 @@ void Interpreter::DoTestEqual(InterpreterAssembler* assembler) {
//
// Test if the value in the <src> register is not equal to the accumulator.
void Interpreter::DoTestNotEqual(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kNotEqual, assembler);
DoBinaryOp(CodeFactory::NotEqual(isolate_), assembler);
}
......
......@@ -285,6 +285,10 @@ Maybe<ComparisonResult> Object::Compare(Handle<Object> x, Handle<Object> y) {
// static
Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) {
// This is the generic version of Abstract Equality Comparison; a version in
// JavaScript land is available in the EqualStub and NotEqualStub. Whenever
// you change something functionality wise in here, remember to update the
// TurboFan code stubs as well.
while (true) {
if (x->IsNumber()) {
if (y->IsNumber()) {
......
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