Commit 5043ab6f authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Move Array.isArray to JSCallReducer/JSTypedLowering

This CL introduces a JSOperator for Array.isArray and moves the
corresponding lowering to JSCallReducer and JSTypedLowering.

Bug: v8:7340, v8:7250
Change-Id: Iaa7ced2ad34bec8cccc9da1041007261168cf4b3
Reviewed-on: https://chromium-review.googlesource.com/1025092
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52757}
parent e698cd36
......@@ -115,116 +115,6 @@ JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph,
native_context_(native_context),
type_cache_(TypeCache::Get()) {}
// ES6 section 22.1.2.2 Array.isArray ( arg )
Reduction JSBuiltinReducer::ReduceArrayIsArray(Node* node) {
// We certainly know that undefined is not an array.
if (node->op()->ValueInputCount() < 3) {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value);
return Replace(value);
}
Node* value = NodeProperties::GetValueInput(node, 2);
Type* value_type = NodeProperties::GetType(value);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Constant-fold based on {value} type.
if (value_type->Is(Type::Array())) {
Node* value = jsgraph()->TrueConstant();
ReplaceWithValue(node, value);
return Replace(value);
} else if (!value_type->Maybe(Type::ArrayOrProxy())) {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value);
return Replace(value);
}
int count = 0;
Node* values[5];
Node* effects[5];
Node* controls[4];
// Check if the {value} is a Smi.
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
control =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
// The {value} is a Smi.
controls[count] = graph()->NewNode(common()->IfTrue(), control);
effects[count] = effect;
values[count] = jsgraph()->FalseConstant();
count++;
control = graph()->NewNode(common()->IfFalse(), control);
// Load the {value}s instance type.
Node* value_map = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
Node* value_instance_type = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
effect, control);
// Check if the {value} is a JSArray.
check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
jsgraph()->Constant(JS_ARRAY_TYPE));
control = graph()->NewNode(common()->Branch(), check, control);
// The {value} is a JSArray.
controls[count] = graph()->NewNode(common()->IfTrue(), control);
effects[count] = effect;
values[count] = jsgraph()->TrueConstant();
count++;
control = graph()->NewNode(common()->IfFalse(), control);
// Check if the {value} is a JSProxy.
check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
jsgraph()->Constant(JS_PROXY_TYPE));
control =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
// The {value} is neither a JSArray nor a JSProxy.
controls[count] = graph()->NewNode(common()->IfFalse(), control);
effects[count] = effect;
values[count] = jsgraph()->FalseConstant();
count++;
control = graph()->NewNode(common()->IfTrue(), control);
// Let the %ArrayIsArray runtime function deal with the JSProxy {value}.
value = effect = control =
graph()->NewNode(javascript()->CallRuntime(Runtime::kArrayIsArray), value,
context, frame_state, effect, control);
NodeProperties::SetType(value, Type::Boolean());
// Update potential {IfException} uses of {node} to point to the above
// %ArrayIsArray runtime call node instead.
Node* on_exception = nullptr;
if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
NodeProperties::ReplaceControlInput(on_exception, control);
NodeProperties::ReplaceEffectInput(on_exception, effect);
control = graph()->NewNode(common()->IfSuccess(), control);
Revisit(on_exception);
}
// The {value} is neither a JSArray nor a JSProxy.
controls[count] = control;
effects[count] = effect;
values[count] = value;
count++;
control = graph()->NewNode(common()->Merge(count), count, controls);
effects[count] = control;
values[count] = control;
effect = graph()->NewNode(common()->EffectPhi(count), count + 1, effects);
value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count),
count + 1, values);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
// ES6 section 20.3.3.1 Date.now ( )
Reduction JSBuiltinReducer::ReduceDateNow(Node* node) {
NodeProperties::RemoveValueInputs(node);
......@@ -272,8 +162,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
if (!r.HasBuiltinFunctionId()) return NoChange();
if (!r.BuiltinCanBeInlined()) return NoChange();
switch (r.GetBuiltinFunctionId()) {
case kArrayIsArray:
return ReduceArrayIsArray(node);
case kDateNow:
return ReduceDateNow(node);
case kDateGetTime:
......
......@@ -39,8 +39,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction Reduce(Node* node) final;
private:
Reduction ReduceArrayIsArray(Node* node);
Reduction ReduceDateNow(Node* node);
Reduction ReduceDateGetTime(Node* node);
Reduction ReduceNumberParseInt(Node* node);
......
......@@ -3384,6 +3384,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReduceArrayIterator(node, IterationKind::kValues);
case Builtins::kArrayIteratorPrototypeNext:
return ReduceArrayIteratorPrototypeNext(node);
case Builtins::kArrayIsArray:
return ReduceArrayIsArray(node);
case Builtins::kArrayBufferIsView:
return ReduceArrayBufferIsView(node);
case Builtins::kDataViewPrototypeGetByteLength:
......@@ -4635,6 +4637,30 @@ Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) {
return Replace(value);
}
// ES6 section 22.1.2.2 Array.isArray ( arg )
Reduction JSCallReducer::ReduceArrayIsArray(Node* node) {
// We certainly know that undefined is not an array.
if (node->op()->ValueInputCount() < 3) {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value);
return Replace(value);
}
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* object = NodeProperties::GetValueInput(node, 2);
node->ReplaceInput(0, object);
node->ReplaceInput(1, context);
node->ReplaceInput(2, frame_state);
node->ReplaceInput(3, effect);
node->ReplaceInput(4, control);
node->TrimInputCount(5);
NodeProperties::ChangeOp(node, javascript()->ObjectIsArray());
return Changed(node);
}
Reduction JSCallReducer::ReduceArrayIterator(Node* node, IterationKind kind) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
......
......@@ -92,6 +92,7 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceArrayPrototypePush(Node* node);
Reduction ReduceArrayPrototypePop(Node* node);
Reduction ReduceArrayPrototypeShift(Node* node);
Reduction ReduceArrayIsArray(Node* node);
enum class ArrayIteratorKind { kArray, kTypedArray };
Reduction ReduceArrayIterator(Node* node, IterationKind kind);
Reduction ReduceArrayIteratorPrototypeNext(Node* node);
......
......@@ -389,6 +389,10 @@ void JSGenericLowering::LowerJSCreateBoundFunction(Node* node) {
UNREACHABLE(); // Eliminated in typed lowering.
}
void JSGenericLowering::LowerJSObjectIsArray(Node* node) {
UNREACHABLE(); // Eliminated in typed lowering.
}
void JSGenericLowering::LowerJSCreateObject(Node* node) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
Callable callable = Builtins::CallableFor(
......
......@@ -627,6 +627,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(CreatePromise, Operator::kEliminatable, 0, 1) \
V(CreateTypedArray, Operator::kNoProperties, 5, 1) \
V(CreateObject, Operator::kNoWrite, 1, 1) \
V(ObjectIsArray, Operator::kNoProperties, 1, 1) \
V(HasProperty, Operator::kNoProperties, 2, 1) \
V(HasInPrototypeChain, Operator::kNoProperties, 2, 1) \
V(OrdinaryHasInstance, Operator::kNoProperties, 2, 1) \
......
......@@ -839,6 +839,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* CreateWithContext(const Handle<ScopeInfo>& scope_info);
const Operator* CreateBlockContext(const Handle<ScopeInfo>& scpope_info);
const Operator* ObjectIsArray();
private:
Zone* zone() const { return zone_; }
......
......@@ -2117,6 +2117,109 @@ Reduction JSTypedLowering::ReduceJSGeneratorRestoreInputOrDebugPos(Node* node) {
return Changed(node);
}
Reduction JSTypedLowering::ReduceObjectIsArray(Node* node) {
Node* value = NodeProperties::GetValueInput(node, 0);
Type* value_type = NodeProperties::GetType(value);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Constant-fold based on {value} type.
if (value_type->Is(Type::Array())) {
Node* value = jsgraph()->TrueConstant();
ReplaceWithValue(node, value);
return Replace(value);
} else if (!value_type->Maybe(Type::ArrayOrProxy())) {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value);
return Replace(value);
}
int count = 0;
Node* values[5];
Node* effects[5];
Node* controls[4];
// Check if the {value} is a Smi.
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
control =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
// The {value} is a Smi.
controls[count] = graph()->NewNode(common()->IfTrue(), control);
effects[count] = effect;
values[count] = jsgraph()->FalseConstant();
count++;
control = graph()->NewNode(common()->IfFalse(), control);
// Load the {value}s instance type.
Node* value_map = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
Node* value_instance_type = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
effect, control);
// Check if the {value} is a JSArray.
check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
jsgraph()->Constant(JS_ARRAY_TYPE));
control = graph()->NewNode(common()->Branch(), check, control);
// The {value} is a JSArray.
controls[count] = graph()->NewNode(common()->IfTrue(), control);
effects[count] = effect;
values[count] = jsgraph()->TrueConstant();
count++;
control = graph()->NewNode(common()->IfFalse(), control);
// Check if the {value} is a JSProxy.
check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
jsgraph()->Constant(JS_PROXY_TYPE));
control =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
// The {value} is neither a JSArray nor a JSProxy.
controls[count] = graph()->NewNode(common()->IfFalse(), control);
effects[count] = effect;
values[count] = jsgraph()->FalseConstant();
count++;
control = graph()->NewNode(common()->IfTrue(), control);
// Let the %ArrayIsArray runtime function deal with the JSProxy {value}.
value = effect = control =
graph()->NewNode(javascript()->CallRuntime(Runtime::kArrayIsArray), value,
context, frame_state, effect, control);
NodeProperties::SetType(value, Type::Boolean());
// Update potential {IfException} uses of {node} to point to the above
// %ArrayIsArray runtime call node instead.
Node* on_exception = nullptr;
if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
NodeProperties::ReplaceControlInput(on_exception, control);
NodeProperties::ReplaceEffectInput(on_exception, effect);
control = graph()->NewNode(common()->IfSuccess(), control);
Revisit(on_exception);
}
// The {value} is neither a JSArray nor a JSProxy.
controls[count] = control;
effects[count] = effect;
values[count] = value;
count++;
control = graph()->NewNode(common()->Merge(count), count, controls);
effects[count] = control;
values[count] = control;
effect = graph()->NewNode(common()->EffectPhi(count), count + 1, effects);
value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count),
count + 1, values);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
Reduction JSTypedLowering::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSEqual:
......@@ -2219,6 +2322,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
case IrOpcode::kSpeculativeNumberLessThan:
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
return ReduceSpeculativeNumberComparison(node);
case IrOpcode::kJSObjectIsArray:
return ReduceObjectIsArray(node);
default:
break;
}
......
......@@ -85,6 +85,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
Reduction ReduceSpeculativeNumberMultiply(Node* node);
Reduction ReduceSpeculativeNumberBinop(Node* node);
Reduction ReduceSpeculativeNumberComparison(Node* node);
Reduction ReduceObjectIsArray(Node* node);
// Helper for ReduceJSLoadModule and ReduceJSStoreModule.
Node* BuildGetModuleCell(Node* node);
......
......@@ -207,6 +207,7 @@
V(JSRejectPromise) \
V(JSResolvePromise) \
V(JSStackCheck) \
V(JSObjectIsArray) \
V(JSDebugger)
#define JS_OP_LIST(V) \
......
......@@ -121,6 +121,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSRejectPromise:
case IrOpcode::kJSResolvePromise:
case IrOpcode::kJSPerformPromiseThen:
case IrOpcode::kJSObjectIsArray:
return true;
default:
......
......@@ -1478,6 +1478,10 @@ Type* Typer::Visitor::TypeJSConstructWithSpread(Node* node) {
return Type::Receiver();
}
Type* Typer::Visitor::TypeJSObjectIsArray(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::JSCallTyper(Type* fun, Typer* t) {
if (fun->IsHeapConstant() && fun->AsHeapConstant()->Value()->IsJSFunction()) {
Handle<JSFunction> function =
......
......@@ -899,6 +899,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 1, Type::Any());
CheckTypeIs(node, Type::Undefined());
break;
case IrOpcode::kJSObjectIsArray:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kComment:
case IrOpcode::kDebugAbort:
......
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