Commit e96c1431 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Optimize typeof in abstract/strict equality comparison.

Add support to optimize certain comparisons of typeof with known
strings to utilize the existing ObjectIs<Type> predicates. Also
add a new ObjectIsCallable, which is used to optimize the common
typeof x === "function" pattern.

R=jarin@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#35562}
parent eb89a753
......@@ -49,6 +49,8 @@ Reduction ChangeLowering::Reduce(Node* node) {
return StoreElement(node);
case IrOpcode::kAllocate:
return Allocate(node);
case IrOpcode::kObjectIsCallable:
return ObjectIsCallable(node);
case IrOpcode::kObjectIsNumber:
return ObjectIsNumber(node);
case IrOpcode::kObjectIsReceiver:
......@@ -652,6 +654,29 @@ Node* ChangeLowering::LoadMapInstanceType(Node* map) {
graph()->start(), graph()->start());
}
Reduction ChangeLowering::ObjectIsCallable(Node* node) {
Node* input = NodeProperties::GetValueInput(node, 0);
// TODO(bmeurer): Optimize somewhat based on input type.
Node* check = IsSmi(input);
Node* branch = graph()->NewNode(common()->Branch(), check, graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue = jsgraph()->Int32Constant(0);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = graph()->NewNode(
machine()->Word32Equal(),
jsgraph()->Uint32Constant(1 << Map::kIsCallable),
graph()->NewNode(machine()->Word32And(),
LoadMapBitField(LoadHeapObjectMap(input, if_false)),
jsgraph()->Uint32Constant((1 << Map::kIsCallable) |
(1 << Map::kIsUndetectable))));
Node* control = graph()->NewNode(common()->Merge(2), if_true, if_false);
node->ReplaceInput(0, vtrue);
node->AppendInput(graph()->zone(), vfalse);
node->AppendInput(graph()->zone(), control);
NodeProperties::ChangeOp(node, common()->Phi(MachineRepresentation::kBit, 2));
return Changed(node);
}
Reduction ChangeLowering::ObjectIsNumber(Node* node) {
Node* input = NodeProperties::GetValueInput(node, 0);
// TODO(bmeurer): Optimize somewhat based on input type.
......
......@@ -61,6 +61,7 @@ class ChangeLowering final : public Reducer {
Node* LoadMapBitField(Node* map);
Node* LoadMapInstanceType(Node* map);
Reduction ObjectIsCallable(Node* node);
Reduction ObjectIsNumber(Node* node);
Reduction ObjectIsReceiver(Node* node);
Reduction ObjectIsSmi(Node* node);
......
......@@ -495,10 +495,54 @@ Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
return NoChange(); // Keep a generic comparison.
}
Reduction JSTypedLowering::ReduceJSEqualTypeOf(Node* node, bool invert) {
HeapObjectBinopMatcher m(node);
if (m.left().IsJSTypeOf() && m.right().HasValue() &&
m.right().Value()->IsString()) {
Node* replacement;
Node* input = m.left().InputAt(0);
Handle<String> value = Handle<String>::cast(m.right().Value());
if (String::Equals(value, factory()->boolean_string())) {
replacement = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged),
graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), input,
jsgraph()->TrueConstant()),
jsgraph()->TrueConstant(),
graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), input,
jsgraph()->FalseConstant()));
} else if (String::Equals(value, factory()->function_string())) {
replacement = graph()->NewNode(simplified()->ObjectIsCallable(), input);
} else if (String::Equals(value, factory()->number_string())) {
replacement = graph()->NewNode(simplified()->ObjectIsNumber(), input);
} else if (String::Equals(value, factory()->string_string())) {
replacement = graph()->NewNode(simplified()->ObjectIsString(), input);
} else if (String::Equals(value, factory()->undefined_string())) {
replacement = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged),
graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), input,
jsgraph()->NullConstant()),
jsgraph()->FalseConstant(),
graph()->NewNode(simplified()->ObjectIsUndetectable(), input));
} else {
return NoChange();
}
if (invert) {
replacement = graph()->NewNode(simplified()->BooleanNot(), replacement);
}
return Replace(replacement);
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) {
if (flags() & kDisableBinaryOpReduction) return NoChange();
Reduction const reduction = ReduceJSEqualTypeOf(node, invert);
if (reduction.Changed()) {
ReplaceWithValue(node, reduction.replacement());
return reduction;
}
JSBinopReduction r(this, node);
if (r.BothInputsAre(Type::Number())) {
......@@ -555,6 +599,10 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
return Replace(replacement);
}
}
Reduction const reduction = ReduceJSEqualTypeOf(node, invert);
if (reduction.Changed()) {
return reduction;
}
if (r.OneInputIs(the_hole_type_)) {
return r.ChangeToPureOperator(simplified()->ReferenceEqual(the_hole_type_),
invert);
......
......@@ -59,6 +59,7 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSInstanceOf(Node* node);
Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSStoreContext(Node* node);
Reduction ReduceJSEqualTypeOf(Node* node, bool invert);
Reduction ReduceJSEqual(Node* node, bool invert);
Reduction ReduceJSStrictEqual(Node* node, bool invert);
Reduction ReduceJSToBoolean(Node* node);
......
......@@ -253,7 +253,8 @@ typedef BinopMatcher<UintPtrMatcher, UintPtrMatcher> UintPtrBinopMatcher;
typedef BinopMatcher<Float32Matcher, Float32Matcher> Float32BinopMatcher;
typedef BinopMatcher<Float64Matcher, Float64Matcher> Float64BinopMatcher;
typedef BinopMatcher<NumberMatcher, NumberMatcher> NumberBinopMatcher;
typedef BinopMatcher<HeapObjectMatcher, HeapObjectMatcher>
HeapObjectBinopMatcher;
template <class BinopMatcher, IrOpcode::Value kMulOpcode,
IrOpcode::Value kShiftOpcode>
......
......@@ -211,6 +211,7 @@
V(StoreField) \
V(StoreBuffer) \
V(StoreElement) \
V(ObjectIsCallable) \
V(ObjectIsNumber) \
V(ObjectIsReceiver) \
V(ObjectIsSmi) \
......
......@@ -1189,6 +1189,7 @@ class RepresentationSelector {
SetOutput(node, MachineRepresentation::kNone);
break;
}
case IrOpcode::kObjectIsCallable:
case IrOpcode::kObjectIsNumber:
case IrOpcode::kObjectIsReceiver:
case IrOpcode::kObjectIsSmi:
......
......@@ -190,6 +190,7 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
V(ChangeFloat64ToTagged, Operator::kNoProperties, 1) \
V(ChangeBoolToBit, Operator::kNoProperties, 1) \
V(ChangeBitToBool, Operator::kNoProperties, 1) \
V(ObjectIsCallable, Operator::kNoProperties, 1) \
V(ObjectIsNumber, Operator::kNoProperties, 1) \
V(ObjectIsReceiver, Operator::kNoProperties, 1) \
V(ObjectIsSmi, Operator::kNoProperties, 1) \
......
......@@ -168,6 +168,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* ChangeBoolToBit();
const Operator* ChangeBitToBool();
const Operator* ObjectIsCallable();
const Operator* ObjectIsNumber();
const Operator* ObjectIsReceiver();
const Operator* ObjectIsSmi();
......
......@@ -247,6 +247,7 @@ class Typer::Visitor : public Reducer {
static Type* NumberToInt32(Type*, Typer*);
static Type* NumberToUint32(Type*, Typer*);
static Type* ObjectIsCallable(Type*, Typer*);
static Type* ObjectIsNumber(Type*, Typer*);
static Type* ObjectIsReceiver(Type*, Typer*);
static Type* ObjectIsSmi(Type*, Typer*);
......@@ -557,6 +558,11 @@ Type* Typer::Visitor::NumberToUint32(Type* type, Typer* t) {
// Type checks.
Type* Typer::Visitor::ObjectIsCallable(Type* type, Typer* t) {
if (type->Is(Type::Function())) return t->singleton_true_;
if (type->Is(Type::Primitive())) return t->singleton_false_;
return Type::Boolean();
}
Type* Typer::Visitor::ObjectIsNumber(Type* type, Typer* t) {
if (type->Is(Type::Number())) return t->singleton_true_;
......@@ -1988,6 +1994,9 @@ Type* Typer::Visitor::TypeStoreElement(Node* node) {
return nullptr;
}
Type* Typer::Visitor::TypeObjectIsCallable(Node* node) {
return TypeUnaryOp(node, ObjectIsCallable);
}
Type* Typer::Visitor::TypeObjectIsNumber(Node* node) {
return TypeUnaryOp(node, ObjectIsNumber);
......
......@@ -746,6 +746,7 @@ void Verifier::Visitor::Check(Node* node) {
CheckUpperIs(node, Type::Boolean());
break;
}
case IrOpcode::kObjectIsCallable:
case IrOpcode::kObjectIsNumber:
case IrOpcode::kObjectIsReceiver:
case IrOpcode::kObjectIsSmi:
......
......@@ -25,62 +25,138 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax
var undetectable = %GetUndetectable();
// Number
assertTrue(typeof 0 == 'number');
assertTrue(typeof 0 === 'number');
assertFalse(typeof 0 != 'number');
assertFalse(typeof 0 !== 'number');
assertTrue(typeof 1.2 == 'number');
assertTrue(typeof 1.2 === 'number');
assertFalse(typeof 1.2 != 'number');
assertFalse(typeof 1.2 !== 'number');
assertTrue(typeof 'x' != 'number');
assertTrue(typeof 'x' !== 'number');
assertFalse(typeof 'x' == 'number');
assertFalse(typeof 'x' === 'number');
assertTrue(typeof Object() != 'number');
assertTrue(typeof Object() !== 'number');
assertFalse(typeof Object() == 'number');
assertFalse(typeof Object() === 'number');
// String
assertTrue(typeof 'x' == 'string');
assertTrue(typeof 'x' === 'string');
assertFalse(typeof 'x' != 'string');
assertFalse(typeof 'x' !== 'string');
assertTrue(typeof ('x' + 'x') == 'string');
assertTrue(typeof ('x' + 'x') === 'string');
assertFalse(typeof ('x' + 'x') != 'string');
assertFalse(typeof ('x' + 'x') !== 'string');
assertTrue(typeof 1 != 'string');
assertTrue(typeof 1 !== 'string');
assertFalse(typeof 1 == 'string');
assertFalse(typeof 1 === 'string');
assertTrue(typeof Object() != 'string');
assertTrue(typeof Object() !== 'string');
assertFalse(typeof Object() == 'string');
assertFalse(typeof Object() === 'string');
// Boolean
assertTrue(typeof true == 'boolean');
assertTrue(typeof true === 'boolean');
assertFalse(typeof true != 'boolean');
assertFalse(typeof true !== 'boolean');
assertTrue(typeof false == 'boolean');
assertTrue(typeof false === 'boolean');
assertFalse(typeof false != 'boolean');
assertFalse(typeof false !== 'boolean');
assertTrue(typeof 1 != 'boolean');
assertTrue(typeof 1 !== 'boolean');
assertFalse(typeof 1 == 'boolean');
assertFalse(typeof 1 === 'boolean');
assertTrue(typeof 'x' != 'boolean');
assertTrue(typeof 'x' !== 'boolean');
assertFalse(typeof 'x' == 'boolean');
assertFalse(typeof 'x' === 'boolean');
assertTrue(typeof Object() != 'boolean');
assertTrue(typeof Object() !== 'boolean');
assertFalse(typeof Object() == 'boolean');
assertFalse(typeof Object() === 'boolean');
// Undefined
assertTrue(typeof void 0 == 'undefined');
assertTrue(typeof void 0 === 'undefined');
assertFalse(typeof void 0 != 'undefined');
assertFalse(typeof void 0 !== 'undefined');
assertTrue(typeof 1 != 'undefined');
assertTrue(typeof 1 !== 'undefined');
assertFalse(typeof 1 == 'undefined');
assertFalse(typeof 1 === 'undefined');
assertTrue(typeof null != 'undefined');
assertTrue(typeof null !== 'undefined');
assertFalse(typeof null == 'undefined');
assertFalse(typeof null === 'undefined');
assertTrue(typeof Object() != 'undefined');
assertTrue(typeof Object() !== 'undefined');
assertFalse(typeof Object() == 'undefined');
assertFalse(typeof Object() === 'undefined');
assertTrue(typeof undetectable == 'undefined');
assertTrue(typeof undetectable === 'undefined');
assertFalse(typeof undetectable != 'undefined');
assertFalse(typeof undetectable !== 'undefined');
// Function
assertTrue(typeof Object == 'function');
assertTrue(typeof Object === 'function');
assertFalse(typeof Object != 'function');
assertFalse(typeof Object !== 'function');
assertTrue(typeof 1 != 'function');
assertTrue(typeof 1 !== 'function');
assertFalse(typeof 1 == 'function');
assertFalse(typeof 1 === 'function');
assertTrue(typeof Object() != 'function');
assertTrue(typeof Object() !== 'function');
assertFalse(typeof Object() == 'function');
assertFalse(typeof Object() === 'function');
assertTrue(typeof undetectable != 'function');
assertTrue(typeof undetectable !== 'function');
assertFalse(typeof undetectable == 'function');
assertFalse(typeof undetectable === 'function');
// Object
assertTrue(typeof Object() == 'object');
assertTrue(typeof Object() === 'object');
assertFalse(typeof Object() != 'object');
assertFalse(typeof Object() !== 'object');
assertTrue(typeof new String('x') == 'object');
assertTrue(typeof new String('x') === 'object');
assertFalse(typeof new String('x') != 'object');
assertFalse(typeof new String('x') !== 'object');
assertTrue(typeof ['x'] == 'object');
assertTrue(typeof ['x'] === 'object');
assertFalse(typeof ['x'] != 'object');
assertFalse(typeof ['x'] !== 'object');
assertTrue(typeof null == 'object');
assertTrue(typeof null === 'object');
assertFalse(typeof null != 'object');
assertFalse(typeof null !== 'object');
assertTrue(typeof 1 != 'object');
assertTrue(typeof 1 !== 'object');
assertFalse(typeof 1 == 'object');
assertFalse(typeof 1 === 'object');
assertTrue(typeof 'x' != 'object');
assertTrue(typeof 'x' !== 'object');
assertFalse(typeof 'x' == 'object'); // bug #674753
assertFalse(typeof 'x' === 'object');
assertTrue(typeof Object != 'object');
assertTrue(typeof Object !== 'object');
assertFalse(typeof Object == 'object');
assertFalse(typeof Object === 'object');
assertTrue(typeof undetectable != 'object');
assertTrue(typeof undetectable !== 'object');
assertFalse(typeof undetectable == 'object');
assertFalse(typeof undetectable === 'object');
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