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

[turbofan] Introduce SameValue operator.

We now represent the SameValue operation explicitly in TurboFan and the
operation can thus participate in all kinds of optimizations. Especially
we get rid of the JSCall node in the general case, which blocks several
optimizations across the call. The general, baseline performance is now
always on par with StrictEqual.

Once the StrictEqual operator is also a simplified operator, we should
start unifying the type based optimizations in SimplifiedLowering.

In the micro-benchmark we go from

  testStrictEqual: 1422 ms.
  testObjectIs: 1520 ms.
  testManualSameValue: 1759 ms.

to

  testStrictEqual: 1426 ms.
  testObjectIs: 1357 ms.
  testManualSameValue: 1766 ms.

which gives the expected result.

Bug: v8:7007
Change-Id: I0de3ff6ff6209ab4c3edb69de6a16e387295a9c8
Reviewed-on: https://chromium-review.googlesource.com/741228Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48994}
parent 03035038
...@@ -700,6 +700,7 @@ namespace internal { ...@@ -700,6 +700,7 @@ namespace internal {
TFC(GreaterThan, Compare, 1) \ TFC(GreaterThan, Compare, 1) \
TFC(GreaterThanOrEqual, Compare, 1) \ TFC(GreaterThanOrEqual, Compare, 1) \
TFC(Equal, Compare, 1) \ TFC(Equal, Compare, 1) \
TFC(SameValue, Compare, 1) \
TFC(StrictEqual, Compare, 1) \ TFC(StrictEqual, Compare, 1) \
\ \
/* Object */ \ /* Object */ \
......
...@@ -593,5 +593,19 @@ TF_BUILTIN(ForInFilter, CodeStubAssembler) { ...@@ -593,5 +593,19 @@ TF_BUILTIN(ForInFilter, CodeStubAssembler) {
Return(UndefinedConstant()); Return(UndefinedConstant());
} }
TF_BUILTIN(SameValue, CodeStubAssembler) {
Node* lhs = Parameter(Descriptor::kLeft);
Node* rhs = Parameter(Descriptor::kRight);
Label if_true(this), if_false(this);
BranchIfSameValue(lhs, rhs, &if_true, &if_false);
BIND(&if_true);
Return(TrueConstant());
BIND(&if_false);
Return(FalseConstant());
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -778,6 +778,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -778,6 +778,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kArrayBufferWasNeutered: case IrOpcode::kArrayBufferWasNeutered:
result = LowerArrayBufferWasNeutered(node); result = LowerArrayBufferWasNeutered(node);
break; break;
case IrOpcode::kSameValue:
result = LowerSameValue(node);
break;
case IrOpcode::kStringFromCharCode: case IrOpcode::kStringFromCharCode:
result = LowerStringFromCharCode(node); result = LowerStringFromCharCode(node);
break; break;
...@@ -2440,6 +2443,20 @@ Node* EffectControlLinearizer::LowerArrayBufferWasNeutered(Node* node) { ...@@ -2440,6 +2443,20 @@ Node* EffectControlLinearizer::LowerArrayBufferWasNeutered(Node* node) {
__ Int32Constant(0)); __ Int32Constant(0));
} }
Node* EffectControlLinearizer::LowerSameValue(Node* node) {
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kSameValue);
Operator::Properties properties = Operator::kEliminatable;
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(desc, __ HeapConstant(callable.code()), lhs, rhs,
__ NoContextConstant());
}
Node* EffectControlLinearizer::LowerStringToNumber(Node* node) { Node* EffectControlLinearizer::LowerStringToNumber(Node* node) {
Node* string = node->InputAt(0); Node* string = node->InputAt(0);
......
...@@ -103,6 +103,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -103,6 +103,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerNewSmiOrObjectElements(Node* node); Node* LowerNewSmiOrObjectElements(Node* node);
Node* LowerNewArgumentsElements(Node* node); Node* LowerNewArgumentsElements(Node* node);
Node* LowerArrayBufferWasNeutered(Node* node); Node* LowerArrayBufferWasNeutered(Node* node);
Node* LowerSameValue(Node* node);
Node* LowerStringToNumber(Node* node); Node* LowerStringToNumber(Node* node);
Node* LowerStringCharAt(Node* node); Node* LowerStringCharAt(Node* node);
Node* LowerStringCharCodeAt(Node* node); Node* LowerStringCharCodeAt(Node* node);
......
...@@ -2325,52 +2325,6 @@ Reduction JSBuiltinReducer::ReduceObjectCreate(Node* node) { ...@@ -2325,52 +2325,6 @@ Reduction JSBuiltinReducer::ReduceObjectCreate(Node* node) {
return Replace(value); return Replace(value);
} }
// ES #sec-object.is
Reduction JSBuiltinReducer::ReduceObjectIs(Node* node) {
// TODO(turbofan): At some point we should probably introduce a new
// SameValue simplified operator (and also a StrictEqual simplified
// operator) and create unified handling in SimplifiedLowering.
JSCallReduction r(node);
if (r.GetJSCallArity() == 2 && r.left() == r.right()) {
// Object.is(x,x) => #true
Node* value = jsgraph()->TrueConstant();
return Replace(value);
} else if (r.InputsMatchTwo(Type::Unique(), Type::Unique())) {
// Object.is(x:Unique,y:Unique) => ReferenceEqual(x,y)
Node* left = r.GetJSCallInput(0);
Node* right = r.GetJSCallInput(1);
Node* value = graph()->NewNode(simplified()->ReferenceEqual(), left, right);
return Replace(value);
} else if (r.InputsMatchTwo(Type::MinusZero(), Type::Any())) {
// Object.is(x:MinusZero,y) => ObjectIsMinusZero(y)
Node* input = r.GetJSCallInput(1);
Node* value = graph()->NewNode(simplified()->ObjectIsMinusZero(), input);
return Replace(value);
} else if (r.InputsMatchTwo(Type::Any(), Type::MinusZero())) {
// Object.is(x,y:MinusZero) => ObjectIsMinusZero(x)
Node* input = r.GetJSCallInput(0);
Node* value = graph()->NewNode(simplified()->ObjectIsMinusZero(), input);
return Replace(value);
} else if (r.InputsMatchTwo(Type::NaN(), Type::Any())) {
// Object.is(x:NaN,y) => ObjectIsNaN(y)
Node* input = r.GetJSCallInput(1);
Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input);
return Replace(value);
} else if (r.InputsMatchTwo(Type::Any(), Type::NaN())) {
// Object.is(x,y:NaN) => ObjectIsNaN(x)
Node* input = r.GetJSCallInput(0);
Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input);
return Replace(value);
} else if (r.InputsMatchTwo(Type::String(), Type::String())) {
// Object.is(x:String,y:String) => StringEqual(x,y)
Node* left = r.GetJSCallInput(0);
Node* right = r.GetJSCallInput(1);
Node* value = graph()->NewNode(simplified()->StringEqual(), left, right);
return Replace(value);
}
return NoChange();
}
// ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits ) // ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits )
Reduction JSBuiltinReducer::ReduceStringFromCharCode(Node* node) { Reduction JSBuiltinReducer::ReduceStringFromCharCode(Node* node) {
JSCallReduction r(node); JSCallReduction r(node);
...@@ -2988,9 +2942,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { ...@@ -2988,9 +2942,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
case kObjectCreate: case kObjectCreate:
reduction = ReduceObjectCreate(node); reduction = ReduceObjectCreate(node);
break; break;
case kObjectIs:
reduction = ReduceObjectIs(node);
break;
case kSetEntries: case kSetEntries:
return ReduceCollectionIterator( return ReduceCollectionIterator(
node, JS_SET_TYPE, Context::SET_KEY_VALUE_ITERATOR_MAP_INDEX); node, JS_SET_TYPE, Context::SET_KEY_VALUE_ITERATOR_MAP_INDEX);
......
...@@ -110,7 +110,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final ...@@ -110,7 +110,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceNumberIsSafeInteger(Node* node); Reduction ReduceNumberIsSafeInteger(Node* node);
Reduction ReduceNumberParseInt(Node* node); Reduction ReduceNumberParseInt(Node* node);
Reduction ReduceObjectCreate(Node* node); Reduction ReduceObjectCreate(Node* node);
Reduction ReduceObjectIs(Node* node);
Reduction ReduceStringCharAt(Node* node); Reduction ReduceStringCharAt(Node* node);
Reduction ReduceStringCharCodeAt(Node* node); Reduction ReduceStringCharCodeAt(Node* node);
Reduction ReduceStringConcat(Node* node); Reduction ReduceStringConcat(Node* node);
......
...@@ -518,6 +518,20 @@ Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node) { ...@@ -518,6 +518,20 @@ Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node) {
return ReduceObjectGetPrototype(node, object); return ReduceObjectGetPrototype(node, object);
} }
// ES section #sec-object.is
Reduction JSCallReducer::ReduceObjectIs(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& params = CallParametersOf(node->op());
int const argc = static_cast<int>(params.arity() - 2);
Node* lhs = (argc >= 1) ? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
Node* rhs = (argc >= 2) ? NodeProperties::GetValueInput(node, 3)
: jsgraph()->UndefinedConstant();
Node* value = graph()->NewNode(simplified()->SameValue(), lhs, rhs);
ReplaceWithValue(node, value);
return Replace(value);
}
// ES6 section B.2.2.1.1 get Object.prototype.__proto__ // ES6 section B.2.2.1.1 get Object.prototype.__proto__
Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) { Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
...@@ -1946,6 +1960,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { ...@@ -1946,6 +1960,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceObjectConstructor(node); return ReduceObjectConstructor(node);
case Builtins::kObjectGetPrototypeOf: case Builtins::kObjectGetPrototypeOf:
return ReduceObjectGetPrototypeOf(node); return ReduceObjectGetPrototypeOf(node);
case Builtins::kObjectIs:
return ReduceObjectIs(node);
case Builtins::kObjectPrototypeGetProto: case Builtins::kObjectPrototypeGetProto:
return ReduceObjectPrototypeGetProto(node); return ReduceObjectPrototypeGetProto(node);
case Builtins::kObjectPrototypeHasOwnProperty: case Builtins::kObjectPrototypeHasOwnProperty:
......
...@@ -64,6 +64,7 @@ class JSCallReducer final : public AdvancedReducer { ...@@ -64,6 +64,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceObjectConstructor(Node* node); Reduction ReduceObjectConstructor(Node* node);
Reduction ReduceObjectGetPrototype(Node* node, Node* object); Reduction ReduceObjectGetPrototype(Node* node, Node* object);
Reduction ReduceObjectGetPrototypeOf(Node* node); Reduction ReduceObjectGetPrototypeOf(Node* node);
Reduction ReduceObjectIs(Node* node);
Reduction ReduceObjectPrototypeGetProto(Node* node); Reduction ReduceObjectPrototypeGetProto(Node* node);
Reduction ReduceObjectPrototypeHasOwnProperty(Node* node); Reduction ReduceObjectPrototypeHasOwnProperty(Node* node);
Reduction ReduceObjectPrototypeIsPrototypeOf(Node* node); Reduction ReduceObjectPrototypeIsPrototypeOf(Node* node);
......
...@@ -234,6 +234,7 @@ ...@@ -234,6 +234,7 @@
V(SpeculativeNumberLessThan) \ V(SpeculativeNumberLessThan) \
V(SpeculativeNumberLessThanOrEqual) \ V(SpeculativeNumberLessThanOrEqual) \
V(ReferenceEqual) \ V(ReferenceEqual) \
V(SameValue) \
V(StringEqual) \ V(StringEqual) \
V(StringLessThan) \ V(StringLessThan) \
V(StringLessThanOrEqual) V(StringLessThanOrEqual)
......
...@@ -1058,6 +1058,42 @@ Type* OperationTyper::FalsifyUndefined(ComparisonOutcome outcome) { ...@@ -1058,6 +1058,42 @@ Type* OperationTyper::FalsifyUndefined(ComparisonOutcome outcome) {
return singleton_true(); return singleton_true();
} }
namespace {
Type* JSType(Type* type) {
if (type->Is(Type::Boolean())) return Type::Boolean();
if (type->Is(Type::String())) return Type::String();
if (type->Is(Type::Number())) return Type::Number();
if (type->Is(Type::Undefined())) return Type::Undefined();
if (type->Is(Type::Null())) return Type::Null();
if (type->Is(Type::Symbol())) return Type::Symbol();
if (type->Is(Type::Receiver())) return Type::Receiver(); // JS "Object"
return Type::Any();
}
} // namespace
Type* OperationTyper::SameValue(Type* lhs, Type* rhs) {
if (!JSType(lhs)->Maybe(JSType(rhs))) return singleton_false();
if (lhs->Is(Type::NaN())) {
if (rhs->Is(Type::NaN())) return singleton_true();
if (!rhs->Maybe(Type::NaN())) return singleton_false();
} else if (rhs->Is(Type::NaN())) {
if (!lhs->Maybe(Type::NaN())) return singleton_false();
}
if (lhs->Is(Type::MinusZero())) {
if (rhs->Is(Type::MinusZero())) return singleton_true();
if (!rhs->Maybe(Type::MinusZero())) return singleton_false();
} else if (rhs->Is(Type::MinusZero())) {
if (!lhs->Maybe(Type::MinusZero())) return singleton_false();
}
if (lhs->Is(Type::OrderedNumber()) && rhs->Is(Type::OrderedNumber()) &&
(lhs->Max() < rhs->Min() || lhs->Min() > rhs->Max())) {
return singleton_false();
}
return Type::Boolean();
}
Type* OperationTyper::CheckFloat64Hole(Type* type) { Type* OperationTyper::CheckFloat64Hole(Type* type) {
if (type->Maybe(Type::Hole())) { if (type->Maybe(Type::Hole())) {
// Turn "the hole" into undefined. // Turn "the hole" into undefined.
......
...@@ -49,6 +49,9 @@ class V8_EXPORT_PRIVATE OperationTyper { ...@@ -49,6 +49,9 @@ class V8_EXPORT_PRIVATE OperationTyper {
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD) SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
#undef DECLARE_METHOD #undef DECLARE_METHOD
// Comparison operators.
Type* SameValue(Type* lhs, Type* rhs);
// Check operators. // Check operators.
Type* CheckFloat64Hole(Type* type); Type* CheckFloat64Hole(Type* type);
Type* CheckNumber(Type* type); Type* CheckNumber(Type* type);
......
...@@ -426,6 +426,7 @@ class RepresentationSelector { ...@@ -426,6 +426,7 @@ class RepresentationSelector {
break; \ break; \
} }
SIMPLIFIED_NUMBER_BINOP_LIST(DECLARE_CASE) SIMPLIFIED_NUMBER_BINOP_LIST(DECLARE_CASE)
DECLARE_CASE(SameValue)
#undef DECLARE_CASE #undef DECLARE_CASE
#define DECLARE_CASE(Name) \ #define DECLARE_CASE(Name) \
...@@ -2349,6 +2350,12 @@ class RepresentationSelector { ...@@ -2349,6 +2350,12 @@ class RepresentationSelector {
} }
return; return;
} }
case IrOpcode::kSameValue: {
if (truncation.IsUnused()) return VisitUnused(node);
VisitBinop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
return;
}
case IrOpcode::kClassOf: case IrOpcode::kClassOf:
case IrOpcode::kTypeOf: { case IrOpcode::kTypeOf: {
return VisitUnop(node, UseInfo::AnyTagged(), return VisitUnop(node, UseInfo::AnyTagged(),
......
...@@ -630,6 +630,7 @@ DeoptimizeReason DeoptimizeReasonOf(const Operator* op) { ...@@ -630,6 +630,7 @@ DeoptimizeReason DeoptimizeReasonOf(const Operator* op) {
V(ObjectIsSymbol, Operator::kNoProperties, 1, 0) \ V(ObjectIsSymbol, Operator::kNoProperties, 1, 0) \
V(ObjectIsUndetectable, Operator::kNoProperties, 1, 0) \ V(ObjectIsUndetectable, Operator::kNoProperties, 1, 0) \
V(ConvertTaggedHoleToUndefined, Operator::kNoProperties, 1, 0) \ V(ConvertTaggedHoleToUndefined, Operator::kNoProperties, 1, 0) \
V(SameValue, Operator::kCommutative, 2, 0) \
V(ReferenceEqual, Operator::kCommutative, 2, 0) \ V(ReferenceEqual, Operator::kCommutative, 2, 0) \
V(StringEqual, Operator::kCommutative, 2, 0) \ V(StringEqual, Operator::kCommutative, 2, 0) \
V(StringLessThan, Operator::kNoProperties, 2, 0) \ V(StringLessThan, Operator::kNoProperties, 2, 0) \
......
...@@ -392,6 +392,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -392,6 +392,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* SpeculativeNumberEqual(NumberOperationHint hint); const Operator* SpeculativeNumberEqual(NumberOperationHint hint);
const Operator* ReferenceEqual(); const Operator* ReferenceEqual();
const Operator* SameValue();
const Operator* TypeOf(); const Operator* TypeOf();
const Operator* ClassOf(); const Operator* ClassOf();
......
...@@ -105,6 +105,8 @@ Reduction TypedOptimization::Reduce(Node* node) { ...@@ -105,6 +105,8 @@ Reduction TypedOptimization::Reduce(Node* node) {
return ReducePhi(node); return ReducePhi(node);
case IrOpcode::kReferenceEqual: case IrOpcode::kReferenceEqual:
return ReduceReferenceEqual(node); return ReduceReferenceEqual(node);
case IrOpcode::kSameValue:
return ReduceSameValue(node);
case IrOpcode::kSelect: case IrOpcode::kSelect:
return ReduceSelect(node); return ReduceSelect(node);
case IrOpcode::kTypeOf: case IrOpcode::kTypeOf:
...@@ -357,6 +359,52 @@ Reduction TypedOptimization::ReduceReferenceEqual(Node* node) { ...@@ -357,6 +359,52 @@ Reduction TypedOptimization::ReduceReferenceEqual(Node* node) {
return NoChange(); return NoChange();
} }
Reduction TypedOptimization::ReduceSameValue(Node* node) {
DCHECK_EQ(IrOpcode::kSameValue, node->opcode());
Node* const lhs = NodeProperties::GetValueInput(node, 0);
Node* const rhs = NodeProperties::GetValueInput(node, 1);
Type* const lhs_type = NodeProperties::GetType(lhs);
Type* const rhs_type = NodeProperties::GetType(rhs);
if (lhs == rhs) {
// SameValue(x,x) => #true
return Replace(jsgraph()->TrueConstant());
} else if (lhs_type->Is(Type::Unique()) && rhs_type->Is(Type::Unique())) {
// SameValue(x:unique,y:unique) => ReferenceEqual(x,y)
NodeProperties::ChangeOp(node, simplified()->ReferenceEqual());
return Changed(node);
} else if (lhs_type->Is(Type::String()) && rhs_type->Is(Type::String())) {
// SameValue(x:string,y:string) => StringEqual(x,y)
NodeProperties::ChangeOp(node, simplified()->StringEqual());
return Changed(node);
} else if (lhs_type->Is(Type::MinusZero())) {
// SameValue(x:minus-zero,y) => ObjectIsMinusZero(y)
node->RemoveInput(0);
NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero());
return Changed(node);
} else if (rhs_type->Is(Type::MinusZero())) {
// SameValue(x,y:minus-zero) => ObjectIsMinusZero(x)
node->RemoveInput(1);
NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero());
return Changed(node);
} else if (lhs_type->Is(Type::NaN())) {
// SameValue(x:nan,y) => ObjectIsNaN(y)
node->RemoveInput(0);
NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN());
return Changed(node);
} else if (rhs_type->Is(Type::NaN())) {
// SameValue(x,y:nan) => ObjectIsNaN(x)
node->RemoveInput(1);
NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN());
return Changed(node);
} else if (lhs_type->Is(Type::PlainNumber()) &&
rhs_type->Is(Type::PlainNumber())) {
// SameValue(x:plain-number,y:plain-number) => NumberEqual(x,y)
NodeProperties::ChangeOp(node, simplified()->NumberEqual());
return Changed(node);
}
return NoChange();
}
Reduction TypedOptimization::ReduceSelect(Node* node) { Reduction TypedOptimization::ReduceSelect(Node* node) {
DCHECK_EQ(IrOpcode::kSelect, node->opcode()); DCHECK_EQ(IrOpcode::kSelect, node->opcode());
Node* const condition = NodeProperties::GetValueInput(node, 0); Node* const condition = NodeProperties::GetValueInput(node, 0);
......
...@@ -50,6 +50,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final ...@@ -50,6 +50,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final
Reduction ReduceNumberToUint8Clamped(Node* node); Reduction ReduceNumberToUint8Clamped(Node* node);
Reduction ReducePhi(Node* node); Reduction ReducePhi(Node* node);
Reduction ReduceReferenceEqual(Node* node); Reduction ReduceReferenceEqual(Node* node);
Reduction ReduceSameValue(Node* node);
Reduction ReduceSelect(Node* node); Reduction ReduceSelect(Node* node);
Reduction ReduceSpeculativeToNumber(Node* node); Reduction ReduceSpeculativeToNumber(Node* node);
Reduction ReduceCheckNotTaggedHole(Node* node); Reduction ReduceCheckNotTaggedHole(Node* node);
......
...@@ -314,6 +314,7 @@ class Typer::Visitor : public Reducer { ...@@ -314,6 +314,7 @@ class Typer::Visitor : public Reducer {
static Type* NumberLessThanTyper(Type*, Type*, Typer*); static Type* NumberLessThanTyper(Type*, Type*, Typer*);
static Type* NumberLessThanOrEqualTyper(Type*, Type*, Typer*); static Type* NumberLessThanOrEqualTyper(Type*, Type*, Typer*);
static Type* ReferenceEqualTyper(Type*, Type*, Typer*); static Type* ReferenceEqualTyper(Type*, Type*, Typer*);
static Type* SameValueTyper(Type*, Type*, Typer*);
static Type* StringFromCharCodeTyper(Type*, Typer*); static Type* StringFromCharCodeTyper(Type*, Typer*);
static Type* StringFromCodePointTyper(Type*, Typer*); static Type* StringFromCodePointTyper(Type*, Typer*);
...@@ -1822,6 +1823,15 @@ Type* Typer::Visitor::TypeReferenceEqual(Node* node) { ...@@ -1822,6 +1823,15 @@ Type* Typer::Visitor::TypeReferenceEqual(Node* node) {
return TypeBinaryOp(node, ReferenceEqualTyper); return TypeBinaryOp(node, ReferenceEqualTyper);
} }
// static
Type* Typer::Visitor::SameValueTyper(Type* lhs, Type* rhs, Typer* t) {
return t->operation_typer()->SameValue(lhs, rhs);
}
Type* Typer::Visitor::TypeSameValue(Node* node) {
return TypeBinaryOp(node, SameValueTyper);
}
Type* Typer::Visitor::TypeStringEqual(Node* node) { return Type::Boolean(); } Type* Typer::Visitor::TypeStringEqual(Node* node) { return Type::Boolean(); }
Type* Typer::Visitor::TypeStringLessThan(Node* node) { return Type::Boolean(); } Type* Typer::Visitor::TypeStringLessThan(Node* node) { return Type::Boolean(); }
......
...@@ -1030,6 +1030,12 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -1030,6 +1030,12 @@ void Verifier::Visitor::Check(Node* node) {
// (Any, Unique) -> Boolean // (Any, Unique) -> Boolean
CheckTypeIs(node, Type::Boolean()); CheckTypeIs(node, Type::Boolean());
break; break;
case IrOpcode::kSameValue:
// (Any, Any) -> Boolean
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Any());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kObjectIsArrayBufferView: case IrOpcode::kObjectIsArrayBufferView:
case IrOpcode::kObjectIsCallable: case IrOpcode::kObjectIsCallable:
......
...@@ -125,6 +125,15 @@ ...@@ -125,6 +125,15 @@
assertTrue(foo("foo")); assertTrue(foo("foo"));
})(); })();
(function() {
function foo(o) { return Object.is(String(o), "foo"); }
assertFalse(foo("bar"));
assertTrue(foo("foo"));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo("bar"));
assertTrue(foo("foo"));
})();
(function() { (function() {
function foo(o) { return Object.is(o, o); } function foo(o) { return Object.is(o, o); }
assertTrue(foo(-0)); assertTrue(foo(-0));
...@@ -141,3 +150,25 @@ ...@@ -141,3 +150,25 @@
assertTrue(foo([])); assertTrue(foo([]));
assertTrue(foo({})); assertTrue(foo({}));
})(); })();
(function() {
function foo(o) { return Object.is(o|0, 0); }
assertTrue(foo(0));
assertTrue(foo(-0));
assertTrue(foo(NaN));
assertFalse(foo(1));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(0));
assertTrue(foo(-0));
assertTrue(foo(NaN));
assertFalse(foo(1));
})();
(function() {
const s = Symbol();
function foo() { return Object.is(s, Symbol()); }
assertFalse(foo());
assertFalse(foo());
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo());
})();
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