summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/js-call-reducer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/js-call-reducer.cc')
-rw-r--r--deps/v8/src/compiler/js-call-reducer.cc1001
1 files changed, 806 insertions, 195 deletions
diff --git a/deps/v8/src/compiler/js-call-reducer.cc b/deps/v8/src/compiler/js-call-reducer.cc
index d8fcf4553a..c595b360d5 100644
--- a/deps/v8/src/compiler/js-call-reducer.cc
+++ b/deps/v8/src/compiler/js-call-reducer.cc
@@ -9,6 +9,7 @@
#include "src/code-stubs.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/access-builder.h"
+#include "src/compiler/allocation-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
@@ -21,62 +22,6 @@ namespace v8 {
namespace internal {
namespace compiler {
-namespace {
-
-bool CanBePrimitive(Node* node) {
- switch (node->opcode()) {
- case IrOpcode::kJSCreate:
- case IrOpcode::kJSCreateArguments:
- case IrOpcode::kJSCreateArray:
- case IrOpcode::kJSCreateClosure:
- case IrOpcode::kJSCreateEmptyLiteralArray:
- case IrOpcode::kJSCreateEmptyLiteralObject:
- case IrOpcode::kJSCreateIterResultObject:
- case IrOpcode::kJSCreateKeyValueArray:
- case IrOpcode::kJSCreateLiteralArray:
- case IrOpcode::kJSCreateLiteralObject:
- case IrOpcode::kJSCreateLiteralRegExp:
- case IrOpcode::kJSConstructForwardVarargs:
- case IrOpcode::kJSConstruct:
- case IrOpcode::kJSConstructWithArrayLike:
- case IrOpcode::kJSConstructWithSpread:
- case IrOpcode::kJSConvertReceiver:
- case IrOpcode::kJSGetSuperConstructor:
- case IrOpcode::kJSToObject:
- return false;
- case IrOpcode::kHeapConstant: {
- Handle<HeapObject> value = HeapObjectMatcher(node).Value();
- return value->IsPrimitive();
- }
- default:
- return true;
- }
-}
-
-bool CanBeNullOrUndefined(Node* node) {
- if (CanBePrimitive(node)) {
- switch (node->opcode()) {
- case IrOpcode::kJSToBoolean:
- case IrOpcode::kJSToInteger:
- case IrOpcode::kJSToLength:
- case IrOpcode::kJSToName:
- case IrOpcode::kJSToNumber:
- case IrOpcode::kJSToString:
- return false;
- case IrOpcode::kHeapConstant: {
- Handle<HeapObject> value = HeapObjectMatcher(node).Value();
- Isolate* const isolate = value->GetIsolate();
- return value->IsNullOrUndefined(isolate);
- }
- default:
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
Reduction JSCallReducer::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSConstruct:
@@ -136,13 +81,11 @@ Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
- // Replace the {node} with a proper {JSToBoolean} operator.
+ // Replace the {node} with a proper {ToBoolean} operator.
DCHECK_LE(2u, p.arity());
Node* value = (p.arity() == 2) ? jsgraph()->UndefinedConstant()
: NodeProperties::GetValueInput(node, 2);
- Node* context = NodeProperties::GetContextInput(node);
- value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), value,
- context);
+ value = graph()->NewNode(simplified()->ToBoolean(), value);
ReplaceWithValue(node, value);
return Replace(value);
}
@@ -168,10 +111,11 @@ Reduction JSCallReducer::ReduceObjectConstructor(Node* node) {
if (p.arity() < 3) return NoChange();
Node* value = (p.arity() >= 3) ? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
+ Node* effect = NodeProperties::GetEffectInput(node);
// We can fold away the Object(x) call if |x| is definitely not a primitive.
- if (CanBePrimitive(value)) {
- if (!CanBeNullOrUndefined(value)) {
+ if (NodeProperties::CanBePrimitive(value, effect)) {
+ if (!NodeProperties::CanBeNullOrUndefined(value, effect)) {
// Turn the {node} into a {JSToObject} call if we know that
// the {value} cannot be null or undefined.
NodeProperties::ReplaceValueInputs(node, value);
@@ -212,7 +156,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
// If {arguments_list} cannot be null or undefined, we don't need
// to expand this {node} to control-flow.
- if (!CanBeNullOrUndefined(arguments_list)) {
+ if (!NodeProperties::CanBeNullOrUndefined(arguments_list, effect)) {
// Massage the value inputs appropriately.
node->ReplaceInput(0, target);
node->ReplaceInput(1, this_argument);
@@ -301,6 +245,106 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
return reduction.Changed() ? reduction : Changed(node);
}
+// ES section #sec-function.prototype.bind
+Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ // Value inputs to the {node} are as follows:
+ //
+ // - target, which is Function.prototype.bind JSFunction
+ // - receiver, which is the [[BoundTargetFunction]]
+ // - bound_this (optional), which is the [[BoundThis]]
+ // - and all the remaining value inouts are [[BoundArguments]]
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* bound_this = (node->op()->ValueInputCount() < 3)
+ ? jsgraph()->UndefinedConstant()
+ : NodeProperties::GetValueInput(node, 2);
+ Node* context = NodeProperties::GetContextInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+
+ // Ensure that the {receiver} is known to be a JSBoundFunction or
+ // a JSFunction with the same [[Prototype]], and all maps we've
+ // seen for the {receiver} so far indicate that {receiver} is
+ // definitely a constructor or not a constructor.
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
+ DCHECK_NE(0, receiver_maps.size());
+ bool const is_constructor = receiver_maps[0]->is_constructor();
+ Handle<Object> const prototype(receiver_maps[0]->prototype(), isolate());
+ for (Handle<Map> const receiver_map : receiver_maps) {
+ // Check for consistency among the {receiver_maps}.
+ STATIC_ASSERT(LAST_TYPE == LAST_FUNCTION_TYPE);
+ if (receiver_map->prototype() != *prototype) return NoChange();
+ if (receiver_map->is_constructor() != is_constructor) return NoChange();
+ if (receiver_map->instance_type() < FIRST_FUNCTION_TYPE) return NoChange();
+
+ // Disallow binding of slow-mode functions. We need to figure out
+ // whether the length and name property are in the original state.
+ if (receiver_map->is_dictionary_map()) return NoChange();
+
+ // Check whether the length and name properties are still present
+ // as AccessorInfo objects. In that case, their values can be
+ // recomputed even if the actual value of the object changes.
+ // This mirrors the checks done in builtins-function-gen.cc at
+ // runtime otherwise.
+ Handle<DescriptorArray> descriptors(receiver_map->instance_descriptors(),
+ isolate());
+ if (descriptors->length() < 2) return NoChange();
+ if (descriptors->GetKey(JSFunction::kLengthDescriptorIndex) !=
+ isolate()->heap()->length_string()) {
+ return NoChange();
+ }
+ if (!descriptors->GetValue(JSFunction::kLengthDescriptorIndex)
+ ->IsAccessorInfo()) {
+ return NoChange();
+ }
+ if (descriptors->GetKey(JSFunction::kNameDescriptorIndex) !=
+ isolate()->heap()->name_string()) {
+ return NoChange();
+ }
+ if (!descriptors->GetValue(JSFunction::kNameDescriptorIndex)
+ ->IsAccessorInfo()) {
+ return NoChange();
+ }
+ }
+
+ // Setup the map for the resulting JSBoundFunction with the
+ // correct instance {prototype}.
+ Handle<Map> map(
+ is_constructor
+ ? native_context()->bound_function_with_constructor_map()
+ : native_context()->bound_function_without_constructor_map(),
+ isolate());
+ if (map->prototype() != *prototype) {
+ map = Map::TransitionToPrototype(map, prototype);
+ }
+
+ // Make sure we can rely on the {receiver_maps}.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect = graph()->NewNode(
+ simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
+ effect, control);
+ }
+
+ // Replace the {node} with a JSCreateBoundFunction.
+ int const arity = std::max(0, node->op()->ValueInputCount() - 3);
+ int const input_count = 2 + arity + 3;
+ Node** inputs = graph()->zone()->NewArray<Node*>(input_count);
+ inputs[0] = receiver;
+ inputs[1] = bound_this;
+ for (int i = 0; i < arity; ++i) {
+ inputs[2 + i] = NodeProperties::GetValueInput(node, 3 + i);
+ }
+ inputs[2 + arity + 0] = context;
+ inputs[2 + arity + 1] = effect;
+ inputs[2 + arity + 2] = control;
+ Node* value = effect = graph()->NewNode(
+ javascript()->CreateBoundFunction(arity, map), input_count, inputs);
+ ReplaceWithValue(node, value, effect, control);
+ return Replace(value);
+}
// ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
@@ -418,6 +462,20 @@ Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node) {
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__
Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
@@ -490,8 +548,9 @@ Reduction JSCallReducer::ReduceObjectPrototypeHasOwnProperty(Node* node) {
receiver, effect, control);
Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
receiver_map, cache_type);
- effect =
- graph()->NewNode(simplified()->CheckIf(), check, effect, control);
+ effect = graph()->NewNode(
+ simplified()->CheckIf(DeoptimizeReason::kNoReason), check, effect,
+ control);
}
Node* value = jsgraph()->TrueConstant();
ReplaceWithValue(node, value, effect, control);
@@ -589,6 +648,150 @@ Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
return ReduceObjectGetPrototype(node, target);
}
+// ES section #sec-reflect.get
+Reduction JSCallReducer::ReduceReflectGet(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ int arity = static_cast<int>(p.arity() - 2);
+ if (arity != 2) return NoChange();
+ Node* target = NodeProperties::GetValueInput(node, 2);
+ Node* key = NodeProperties::GetValueInput(node, 3);
+ Node* context = NodeProperties::GetContextInput(node);
+ Node* frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+
+ // Check whether {target} is a JSReceiver.
+ Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target);
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
+
+ // Throw an appropriate TypeError if the {target} is not a JSReceiver.
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+ Node* efalse = effect;
+ {
+ if_false = efalse = graph()->NewNode(
+ javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
+ jsgraph()->Constant(MessageTemplate::kCalledOnNonObject),
+ jsgraph()->HeapConstant(
+ factory()->NewStringFromAsciiChecked("Reflect.get")),
+ context, frame_state, efalse, if_false);
+ }
+
+ // Otherwise just use the existing GetPropertyStub.
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+ Node* etrue = effect;
+ Node* vtrue;
+ {
+ Callable callable = CodeFactory::GetProperty(isolate());
+ CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
+ isolate(), graph()->zone(), callable.descriptor(), 0,
+ CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
+ MachineType::AnyTagged(), 1);
+ Node* stub_code = jsgraph()->HeapConstant(callable.code());
+ vtrue = etrue = if_true =
+ graph()->NewNode(common()->Call(desc), stub_code, target, key, context,
+ frame_state, etrue, if_true);
+ }
+
+ // Rewire potential exception edges.
+ Node* on_exception = nullptr;
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
+ // Create appropriate {IfException} and {IfSuccess} nodes.
+ Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true);
+ if_true = graph()->NewNode(common()->IfSuccess(), if_true);
+ Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false);
+ if_false = graph()->NewNode(common()->IfSuccess(), if_false);
+
+ // Join the exception edges.
+ Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse);
+ Node* ephi =
+ graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge);
+ Node* phi =
+ graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
+ extrue, exfalse, merge);
+ ReplaceWithValue(on_exception, phi, ephi, merge);
+ }
+
+ // Connect the throwing path to end.
+ if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
+ NodeProperties::MergeControlToEnd(graph(), common(), if_false);
+
+ // Continue on the regular path.
+ ReplaceWithValue(node, vtrue, etrue, if_true);
+ return Changed(vtrue);
+}
+
+// ES section #sec-reflect.has
+Reduction JSCallReducer::ReduceReflectHas(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ int arity = static_cast<int>(p.arity() - 2);
+ DCHECK_LE(0, arity);
+ Node* target = (arity >= 1) ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+ Node* key = (arity >= 2) ? NodeProperties::GetValueInput(node, 3)
+ : jsgraph()->UndefinedConstant();
+ Node* context = NodeProperties::GetContextInput(node);
+ Node* frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+
+ // Check whether {target} is a JSReceiver.
+ Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target);
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
+
+ // Throw an appropriate TypeError if the {target} is not a JSReceiver.
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+ Node* efalse = effect;
+ {
+ if_false = efalse = graph()->NewNode(
+ javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
+ jsgraph()->Constant(MessageTemplate::kCalledOnNonObject),
+ jsgraph()->HeapConstant(
+ factory()->NewStringFromAsciiChecked("Reflect.has")),
+ context, frame_state, efalse, if_false);
+ }
+
+ // Otherwise just use the existing {JSHasProperty} logic.
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+ Node* etrue = effect;
+ Node* vtrue;
+ {
+ vtrue = etrue = if_true =
+ graph()->NewNode(javascript()->HasProperty(), key, target, context,
+ frame_state, etrue, if_true);
+ }
+
+ // Rewire potential exception edges.
+ Node* on_exception = nullptr;
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
+ // Create appropriate {IfException} and {IfSuccess} nodes.
+ Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true);
+ if_true = graph()->NewNode(common()->IfSuccess(), if_true);
+ Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false);
+ if_false = graph()->NewNode(common()->IfSuccess(), if_false);
+
+ // Join the exception edges.
+ Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse);
+ Node* ephi =
+ graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge);
+ Node* phi =
+ graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
+ extrue, exfalse, merge);
+ ReplaceWithValue(on_exception, phi, ephi, merge);
+ }
+
+ // Connect the throwing path to end.
+ if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
+ NodeProperties::MergeControlToEnd(graph(), common(), if_false);
+
+ // Continue on the regular path.
+ ReplaceWithValue(node, vtrue, etrue, if_true);
+ return Changed(vtrue);
+}
+
bool CanInlineArrayIteratingBuiltin(Handle<Map> receiver_map) {
Isolate* const isolate = receiver_map->GetIsolate();
if (!receiver_map->prototype()->IsJSArray()) return false;
@@ -597,7 +800,7 @@ bool CanInlineArrayIteratingBuiltin(Handle<Map> receiver_map) {
return receiver_map->instance_type() == JS_ARRAY_TYPE &&
IsFastElementsKind(receiver_map->elements_kind()) &&
(!receiver_map->is_prototype_map() || receiver_map->is_stable()) &&
- isolate->IsFastArrayConstructorPrototypeChainIntact() &&
+ isolate->IsNoElementsProtectorIntact() &&
isolate->IsAnyInitialArrayPrototype(receiver_prototype);
}
@@ -649,7 +852,7 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
// Install code dependencies on the {receiver} prototype maps and the
// global array protector cell.
- dependencies()->AssumePropertyCell(factory()->array_protector());
+ dependencies()->AssumePropertyCell(factory()->no_elements_protector());
Node* k = jsgraph()->ZeroConstant();
@@ -663,24 +866,21 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
// Check whether the given callback function is callable. Note that this has
// to happen outside the loop to make sure we also throw on empty arrays.
- Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), fncallback);
- Node* check_branch =
- graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
- Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function, Builtins::kArrayForEachLoopLazyDeoptContinuation,
node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
outer_frame_state, ContinuationFrameStateMode::LAZY);
- Node* check_throw = check_fail = graph()->NewNode(
- javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
- jsgraph()->Constant(MessageTemplate::kCalledNonCallable), fncallback,
- context, check_frame_state, effect, check_fail);
- control = graph()->NewNode(common()->IfTrue(), check_branch);
+ Node* check_fail = nullptr;
+ Node* check_throw = nullptr;
+ WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
+ &control, &check_fail, &check_throw);
// Start the loop.
Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
+ Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
+ NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* vloop = k = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
checkpoint_params[3] = k;
@@ -710,24 +910,7 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
effect, control);
- // Make sure that the access is still in bounds, since the callback could have
- // changed the array's size.
- Node* length = effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)),
- receiver, effect, control);
- k = effect =
- graph()->NewNode(simplified()->CheckBounds(), k, length, effect, control);
-
- // Reload the elements pointer before calling the callback, since the previous
- // callback might have resized the array causing the elements buffer to be
- // re-allocated.
- Node* elements = effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
- effect, control);
-
- Node* element = effect = graph()->NewNode(
- simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
- elements, k, effect, control);
+ Node* element = SafeLoadElement(kind, receiver, control, &effect, &k);
Node* next_k =
graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->Constant(1));
@@ -767,23 +950,8 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
// Rewire potential exception edges.
Node* on_exception = nullptr;
if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
- // Create appropriate {IfException} and {IfSuccess} nodes.
- Node* if_exception0 =
- graph()->NewNode(common()->IfException(), check_throw, check_fail);
- check_fail = graph()->NewNode(common()->IfSuccess(), check_fail);
- Node* if_exception1 =
- graph()->NewNode(common()->IfException(), effect, control);
- control = graph()->NewNode(common()->IfSuccess(), control);
-
- // Join the exception edges.
- Node* merge =
- graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
- Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
- if_exception1, merge);
- Node* phi =
- graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
- if_exception0, if_exception1, merge);
- ReplaceWithValue(on_exception, phi, ephi, merge);
+ RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
+ &check_fail, &control);
}
if (IsHoleyElementsKind(kind)) {
@@ -806,12 +974,13 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
control = if_false;
effect = eloop;
- // The above %ThrowTypeError runtime call is an unconditional throw, making
- // it impossible to return a successful completion in this case. We simply
- // connect the successful completion to the graph end.
- Node* terminate =
+ // Wire up the branch for the case when IsCallable fails for the callback.
+ // Since {check_throw} is an unconditional throw, it's impossible to
+ // return a successful completion. Therefore, we simply connect the successful
+ // completion to the graph end.
+ Node* throw_node =
graph()->NewNode(common()->Throw(), check_throw, check_fail);
- NodeProperties::MergeControlToEnd(graph(), common(), terminate);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control);
return Replace(jsgraph()->UndefinedConstant());
@@ -899,24 +1068,21 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
// Check whether the given callback function is callable. Note that this has
// to happen outside the loop to make sure we also throw on empty arrays.
- Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), fncallback);
- Node* check_branch =
- graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
- Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function, Builtins::kArrayMapLoopLazyDeoptContinuation,
node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
outer_frame_state, ContinuationFrameStateMode::LAZY);
- Node* check_throw = check_fail = graph()->NewNode(
- javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
- jsgraph()->Constant(MessageTemplate::kCalledNonCallable), fncallback,
- context, check_frame_state, effect, check_fail);
- control = graph()->NewNode(common()->IfTrue(), check_branch);
+ Node* check_fail = nullptr;
+ Node* check_throw = nullptr;
+ WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
+ &control, &check_fail, &check_throw);
// Start the loop.
Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
+ Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
+ NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* vloop = k = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
checkpoint_params[4] = k;
@@ -946,24 +1112,7 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
effect, control);
- // Make sure that the access is still in bounds, since the callback could have
- // changed the array's size.
- Node* length = effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
- effect, control);
- k = effect =
- graph()->NewNode(simplified()->CheckBounds(), k, length, effect, control);
-
- // Reload the elements pointer before calling the callback, since the previous
- // callback might have resized the array causing the elements buffer to be
- // re-allocated.
- Node* elements = effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
- effect, control);
-
- Node* element = effect = graph()->NewNode(
- simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
- elements, k, effect, control);
+ Node* element = SafeLoadElement(kind, receiver, control, &effect, &k);
Node* next_k =
graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
@@ -982,23 +1131,8 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
// Rewire potential exception edges.
Node* on_exception = nullptr;
if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
- // Create appropriate {IfException} and {IfSuccess} nodes.
- Node* if_exception0 =
- graph()->NewNode(common()->IfException(), check_throw, check_fail);
- check_fail = graph()->NewNode(common()->IfSuccess(), check_fail);
- Node* if_exception1 =
- graph()->NewNode(common()->IfException(), effect, control);
- control = graph()->NewNode(common()->IfSuccess(), control);
-
- // Join the exception edges.
- Node* merge =
- graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
- Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
- if_exception1, merge);
- Node* phi =
- graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
- if_exception0, if_exception1, merge);
- ReplaceWithValue(on_exception, phi, ephi, merge);
+ RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
+ &check_fail, &control);
}
Handle<Map> double_map(Map::cast(
@@ -1018,19 +1152,370 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
control = if_false;
effect = eloop;
- // The above %ThrowTypeError runtime call is an unconditional throw, making
- // it impossible to return a successful completion in this case. We simply
- // connect the successful completion to the graph end.
- Node* terminate =
+ // Wire up the branch for the case when IsCallable fails for the callback.
+ // Since {check_throw} is an unconditional throw, it's impossible to
+ // return a successful completion. Therefore, we simply connect the successful
+ // completion to the graph end.
+ Node* throw_node =
graph()->NewNode(common()->Throw(), check_throw, check_fail);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
+
+ ReplaceWithValue(node, a, effect, control);
+ return Replace(a);
+}
+
+Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
+ Node* node) {
+ if (!FLAG_turbo_inline_array_builtins) return NoChange();
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+ Node* context = NodeProperties::GetContextInput(node);
+ CallParameters const& p = CallParametersOf(node->op());
+ // Try to determine the {receiver} map.
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* fncallback = node->op()->ValueInputCount() > 2
+ ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+ Node* this_arg = node->op()->ValueInputCount() > 3
+ ? NodeProperties::GetValueInput(node, 3)
+ : jsgraph()->UndefinedConstant();
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result != NodeProperties::kReliableReceiverMaps) {
+ return NoChange();
+ }
+
+ // And ensure that any changes to the Array species constructor cause deopt.
+ if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
+
+ if (receiver_maps.size() == 0) return NoChange();
+
+ const ElementsKind kind = receiver_maps[0]->elements_kind();
+
+ // TODO(danno): Handle holey elements kinds.
+ if (!IsFastPackedElementsKind(kind)) {
+ return NoChange();
+ }
+
+ for (Handle<Map> receiver_map : receiver_maps) {
+ if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
+ return NoChange();
+ }
+ // We can handle different maps, as long as their elements kind are the
+ // same.
+ if (receiver_map->elements_kind() != kind) {
+ return NoChange();
+ }
+ }
+
+ dependencies()->AssumePropertyCell(factory()->species_protector());
+
+ Handle<Map> initial_map(
+ Map::cast(native_context()->GetInitialJSArrayMap(kind)));
+
+ Node* k = jsgraph()->ZeroConstant();
+ Node* to = jsgraph()->ZeroConstant();
+
+ // Make sure the map hasn't changed before we construct the output array.
+ effect = graph()->NewNode(
+ simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
+ effect, control);
+
+ Node* a; // Construct the output array.
+ {
+ AllocationBuilder ab(jsgraph(), effect, control);
+ ab.Allocate(initial_map->instance_size(), NOT_TENURED, Type::Array());
+ ab.Store(AccessBuilder::ForMap(), initial_map);
+ Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
+ ab.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), empty_fixed_array);
+ ab.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array);
+ ab.Store(AccessBuilder::ForJSArrayLength(kind), jsgraph()->ZeroConstant());
+ for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) {
+ ab.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
+ jsgraph()->UndefinedConstant());
+ }
+ a = effect = ab.Finish();
+ }
+
+ Node* original_length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
+ effect, control);
+
+ // Check whether the given callback function is callable. Note that this has
+ // to happen outside the loop to make sure we also throw on empty arrays.
+ Node* check_fail = nullptr;
+ Node* check_throw = nullptr;
+ {
+ // This frame state doesn't ever call the deopt continuation, it's only
+ // necessary to specifiy a continuation in order to handle the exceptional
+ // case. We don't have all the values available to completely fill out
+ // checkpoint_params yet, but that's okay because it'll never be called.
+ // Therefore, "to" is mentioned twice, once standing in for the k_value
+ // value.
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, a, k, original_length, to, to});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayFilterLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+ WireInCallbackIsCallableCheck(fncallback, context, check_frame_state,
+ effect, &control, &check_fail, &check_throw);
+ }
+
+ // Start the loop.
+ Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
+ Node* eloop = effect =
+ graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
+ Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
+ Node* vloop = k = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
+ Node* v_to_loop = to = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTaggedSigned, 2), to, to, loop);
+
+ control = loop;
+ effect = eloop;
+
+ Node* continue_test =
+ graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
+ Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
+ continue_test, control);
+
+ Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
+ Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
+ control = if_true;
+
+ {
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, a, k, original_length, to});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayFilterLoopEagerDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::EAGER);
+
+ effect =
+ graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
+ }
+
+ // Make sure the map hasn't changed during the iteration.
+ effect = graph()->NewNode(
+ simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
+ effect, control);
+
+ Node* element = SafeLoadElement(kind, receiver, control, &effect, &k);
+
+ Node* next_k =
+ graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
+
+ Node* callback_value = nullptr;
+ {
+ // This frame state is dealt with by hand in
+ // Builtins::kArrayFilterLoopLazyDeoptContinuation.
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, a, k, original_length, element, to});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayFilterLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+
+ callback_value = control = effect = graph()->NewNode(
+ javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
+ receiver, context, frame_state, effect, control);
+ }
+
+ // Rewire potential exception edges.
+ Node* on_exception = nullptr;
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
+ RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
+ &check_fail, &control);
+ }
+
+ // We need an eager frame state for right after the callback function
+ // returned, just in case an attempt to grow the output array fails.
+ //
+ // Note that we are intentionally reusing the
+ // Builtins::kArrayFilterLoopLazyDeoptContinuation as an *eager* entry
+ // point in this case. This is safe, because re-evaluating a [ToBoolean]
+ // coercion is safe.
+ {
+ std::vector<Node*> checkpoint_params({receiver, fncallback, this_arg, a, k,
+ original_length, element, to,
+ callback_value});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayFilterLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::EAGER);
+
+ effect =
+ graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
+ }
+
+ // We have to coerce callback_value to boolean, and only store the element in
+ // a if it's true. The checkpoint above protects against the case that
+ // growing {a} fails.
+ to = DoFilterPostCallbackWork(kind, &control, &effect, a, to, element,
+ callback_value);
+ k = next_k;
+
+ loop->ReplaceInput(1, control);
+ vloop->ReplaceInput(1, k);
+ v_to_loop->ReplaceInput(1, to);
+ eloop->ReplaceInput(1, effect);
+
+ control = if_false;
+ effect = eloop;
+
+ // Wire up the branch for the case when IsCallable fails for the callback.
+ // Since {check_throw} is an unconditional throw, it's impossible to
+ // return a successful completion. Therefore, we simply connect the successful
+ // completion to the graph end.
+ Node* throw_node =
+ graph()->NewNode(common()->Throw(), check_throw, check_fail);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
ReplaceWithValue(node, a, effect, control);
return Replace(a);
}
-Reduction JSCallReducer::ReduceCallApiFunction(
- Node* node, Handle<FunctionTemplateInfo> function_template_info) {
+Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control,
+ Node** effect, Node* a, Node* to,
+ Node* element,
+ Node* callback_value) {
+ Node* boolean_result =
+ graph()->NewNode(simplified()->ToBoolean(), callback_value);
+
+ Node* check_boolean_result =
+ graph()->NewNode(simplified()->ReferenceEqual(), boolean_result,
+ jsgraph()->TrueConstant());
+ Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
+ check_boolean_result, *control);
+
+ Node* if_true = graph()->NewNode(common()->IfTrue(), boolean_branch);
+ Node* etrue = *effect;
+ Node* vtrue;
+ {
+ // Load the elements backing store of the {receiver}.
+ Node* elements = etrue = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSObjectElements()), a, etrue,
+ if_true);
+
+ // We know that {to} is in Unsigned31 range here, being smaller than
+ // {original_length} at all times.
+ Node* checked_to =
+ graph()->NewNode(common()->TypeGuard(Type::Unsigned31()), to, if_true);
+ Node* elements_length = etrue = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements,
+ etrue, if_true);
+
+ GrowFastElementsMode mode =
+ IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements
+ : GrowFastElementsMode::kSmiOrObjectElements;
+ elements = etrue =
+ graph()->NewNode(simplified()->MaybeGrowFastElements(mode), a, elements,
+ checked_to, elements_length, etrue, if_true);
+
+ // Update the length of {a}.
+ Node* new_length_a = graph()->NewNode(simplified()->NumberAdd(), checked_to,
+ jsgraph()->OneConstant());
+
+ etrue = graph()->NewNode(
+ simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), a,
+ new_length_a, etrue, if_true);
+
+ // Append the value to the {elements}.
+ etrue = graph()->NewNode(
+ simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)),
+ elements, checked_to, element, etrue, if_true);
+
+ vtrue = new_length_a;
+ }
+
+ Node* if_false = graph()->NewNode(common()->IfFalse(), boolean_branch);
+ Node* efalse = *effect;
+ Node* vfalse = to;
+
+ *control = graph()->NewNode(common()->Merge(2), if_true, if_false);
+ *effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, *control);
+ to = graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2),
+ vtrue, vfalse, *control);
+ return to;
+}
+
+void JSCallReducer::WireInCallbackIsCallableCheck(
+ Node* fncallback, Node* context, Node* check_frame_state, Node* effect,
+ Node** control, Node** check_fail, Node** check_throw) {
+ Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), fncallback);
+ Node* check_branch =
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check, *control);
+ *check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
+ *check_throw = *check_fail = graph()->NewNode(
+ javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
+ jsgraph()->Constant(MessageTemplate::kCalledNonCallable), fncallback,
+ context, check_frame_state, effect, *check_fail);
+ *control = graph()->NewNode(common()->IfTrue(), check_branch);
+}
+
+void JSCallReducer::RewirePostCallbackExceptionEdges(Node* check_throw,
+ Node* on_exception,
+ Node* effect,
+ Node** check_fail,
+ Node** control) {
+ // Create appropriate {IfException} and {IfSuccess} nodes.
+ Node* if_exception0 =
+ graph()->NewNode(common()->IfException(), check_throw, *check_fail);
+ *check_fail = graph()->NewNode(common()->IfSuccess(), *check_fail);
+ Node* if_exception1 =
+ graph()->NewNode(common()->IfException(), effect, *control);
+ *control = graph()->NewNode(common()->IfSuccess(), *control);
+
+ // Join the exception edges.
+ Node* merge =
+ graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
+ Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
+ if_exception1, merge);
+ Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
+ if_exception0, if_exception1, merge);
+ ReplaceWithValue(on_exception, phi, ephi, merge);
+}
+
+Node* JSCallReducer::SafeLoadElement(ElementsKind kind, Node* receiver,
+ Node* control, Node** effect, Node** k) {
+ // Make sure that the access is still in bounds, since the callback could have
+ // changed the array's size.
+ Node* length = *effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
+ *effect, control);
+ *k = *effect = graph()->NewNode(simplified()->CheckBounds(), *k, length,
+ *effect, control);
+
+ // Reload the elements pointer before calling the callback, since the previous
+ // callback might have resized the array causing the elements buffer to be
+ // re-allocated.
+ Node* elements = *effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
+ *effect, control);
+
+ Node* masked_index =
+ graph()->NewNode(simplified()->MaskIndexWithBound(), *k, length);
+
+ Node* element = *effect = graph()->NewNode(
+ simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
+ elements, masked_index, *effect, control);
+ return element;
+}
+
+Reduction JSCallReducer::ReduceCallApiFunction(Node* node,
+ Handle<JSFunction> function) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
int const argc = static_cast<int>(p.arity()) - 2;
@@ -1039,6 +1524,10 @@ Reduction JSCallReducer::ReduceCallApiFunction(
: NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
+ Handle<FunctionTemplateInfo> function_template_info(
+ FunctionTemplateInfo::cast(function->shared()->function_data()));
+ Handle<Context> context(function->context());
+
// CallApiCallbackStub expects the target in a register, so we count it out,
// and counts the receiver as an implicit argument, so we count the receiver
// out too.
@@ -1094,14 +1583,13 @@ Reduction JSCallReducer::ReduceCallApiFunction(
Handle<CallHandlerInfo> call_handler_info(
CallHandlerInfo::cast(function_template_info->call_code()), isolate());
Handle<Object> data(call_handler_info->data(), isolate());
- CallApiCallbackStub stub(isolate(), argc, false);
+ CallApiCallbackStub stub(isolate(), argc);
CallInterfaceDescriptor cid = stub.GetCallInterfaceDescriptor();
CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), cid,
- cid.GetStackParameterCount() + argc +
- 2 /* implicit receiver + accessor_holder */,
+ cid.GetStackParameterCount() + argc + 1 /* implicit receiver */,
CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
- MachineType::AnyTagged(), 1);
+ MachineType::AnyTagged(), 1, Linkage::kNoContext);
ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback()));
Node* holder = lookup == CallOptimization::kHolderFound
? jsgraph()->HeapConstant(api_holder)
@@ -1110,12 +1598,14 @@ Reduction JSCallReducer::ReduceCallApiFunction(
&api_function, ExternalReference::DIRECT_API_CALL, isolate());
node->InsertInput(graph()->zone(), 0,
jsgraph()->HeapConstant(stub.GetCode()));
+ node->ReplaceInput(1, jsgraph()->Constant(context));
node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data));
node->InsertInput(graph()->zone(), 3, holder);
node->InsertInput(graph()->zone(), 4,
jsgraph()->ExternalConstant(function_reference));
- node->InsertInput(graph()->zone(), 5, holder /* as accessor_holder */);
- node->ReplaceInput(6, receiver);
+ node->ReplaceInput(5, receiver);
+ // Remove context input.
+ node->RemoveInput(6 + argc);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
return Changed(node);
}
@@ -1344,9 +1834,9 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
// The above %ThrowTypeError runtime call is an unconditional throw, making
// it impossible to return a successful completion in this case. We simply
// connect the successful completion to the graph end.
- Node* terminate =
+ Node* throw_node =
graph()->NewNode(common()->Throw(), check_throw, check_fail);
- NodeProperties::MergeControlToEnd(graph(), common(), terminate);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
Reduction const reduction = ReduceJSConstruct(node);
return reduction.Changed() ? reduction : Changed(node);
@@ -1385,6 +1875,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
Node* target = NodeProperties::GetValueInput(node, 0);
Node* control = NodeProperties::GetControlInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
+ size_t arity = p.arity();
+ DCHECK_LE(2u, arity);
// Try to specialize JSCall {node}s with constant {target}s.
HeapObjectMatcher m(target);
@@ -1413,6 +1905,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceBooleanConstructor(node);
case Builtins::kFunctionPrototypeApply:
return ReduceFunctionPrototypeApply(node);
+ case Builtins::kFastFunctionPrototypeBind:
+ return ReduceFunctionPrototypeBind(node);
case Builtins::kFunctionPrototypeCall:
return ReduceFunctionPrototypeCall(node);
case Builtins::kFunctionPrototypeHasInstance:
@@ -1423,6 +1917,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceObjectConstructor(node);
case Builtins::kObjectGetPrototypeOf:
return ReduceObjectGetPrototypeOf(node);
+ case Builtins::kObjectIs:
+ return ReduceObjectIs(node);
case Builtins::kObjectPrototypeGetProto:
return ReduceObjectPrototypeGetProto(node);
case Builtins::kObjectPrototypeHasOwnProperty:
@@ -1433,12 +1929,18 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceReflectApply(node);
case Builtins::kReflectConstruct:
return ReduceReflectConstruct(node);
+ case Builtins::kReflectGet:
+ return ReduceReflectGet(node);
case Builtins::kReflectGetPrototypeOf:
return ReduceReflectGetPrototypeOf(node);
+ case Builtins::kReflectHas:
+ return ReduceReflectHas(node);
case Builtins::kArrayForEach:
return ReduceArrayForEach(function, node);
case Builtins::kArrayMap:
return ReduceArrayMap(function, node);
+ case Builtins::kArrayFilter:
+ return ReduceArrayFilter(function, node);
case Builtins::kReturnReceiver:
return ReduceReturnReceiver(node);
default:
@@ -1446,9 +1948,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
}
if (!FLAG_runtime_stats && shared->IsApiFunction()) {
- Handle<FunctionTemplateInfo> function_template_info(
- FunctionTemplateInfo::cast(shared->function_data()), isolate());
- return ReduceCallApiFunction(node, function_template_info);
+ return ReduceCallApiFunction(node, function);
}
} else if (m.Value()->IsJSBoundFunction()) {
Handle<JSBoundFunction> function =
@@ -1458,13 +1958,10 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
Handle<Object> bound_this(function->bound_this(), isolate());
Handle<FixedArray> bound_arguments(function->bound_arguments(),
isolate());
- CallParameters const& p = CallParametersOf(node->op());
ConvertReceiverMode const convert_mode =
(bound_this->IsNullOrUndefined(isolate()))
? ConvertReceiverMode::kNullOrUndefined
: ConvertReceiverMode::kNotNullOrUndefined;
- size_t arity = p.arity();
- DCHECK_LE(2u, arity);
// Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]].
NodeProperties::ReplaceValueInput(
node, jsgraph()->Constant(bound_target_function), 0);
@@ -1490,6 +1987,40 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return NoChange();
}
+ // If {target} is the result of a JSCreateBoundFunction operation,
+ // we can just fold the construction and call the bound target
+ // function directly instead.
+ if (target->opcode() == IrOpcode::kJSCreateBoundFunction) {
+ Node* bound_target_function = NodeProperties::GetValueInput(target, 0);
+ Node* bound_this = NodeProperties::GetValueInput(target, 1);
+ int const bound_arguments_length =
+ static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity());
+
+ // Patch the {node} to use [[BoundTargetFunction]] and [[BoundThis]].
+ NodeProperties::ReplaceValueInput(node, bound_target_function, 0);
+ NodeProperties::ReplaceValueInput(node, bound_this, 1);
+
+ // Insert the [[BoundArguments]] for {node}.
+ for (int i = 0; i < bound_arguments_length; ++i) {
+ Node* value = NodeProperties::GetValueInput(target, 2 + i);
+ node->InsertInput(graph()->zone(), 2 + i, value);
+ arity++;
+ }
+
+ // Update the JSCall operator on {node}.
+ ConvertReceiverMode const convert_mode =
+ NodeProperties::CanBeNullOrUndefined(bound_this, effect)
+ ? ConvertReceiverMode::kAny
+ : ConvertReceiverMode::kNotNullOrUndefined;
+ NodeProperties::ChangeOp(
+ node, javascript()->Call(arity, p.frequency(), VectorSlotPair(),
+ convert_mode));
+
+ // Try to further reduce the JSCall {node}.
+ Reduction const reduction = ReduceJSCall(node);
+ return reduction.Changed() ? reduction : Changed(node);
+ }
+
// Extract feedback from the {node} using the CallICNexus.
if (!p.feedback().IsValid()) return NoChange();
CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
@@ -1508,7 +2039,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
if (!ShouldUseCallICFeedback(target)) return NoChange();
Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
- if (cell->value()->IsJSFunction()) {
+ if (cell->value()->IsCallable()) {
Node* target_function =
jsgraph()->Constant(handle(cell->value(), isolate()));
@@ -1516,7 +2047,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
target_function);
effect =
- graph()->NewNode(simplified()->CheckIf(), check, effect, control);
+ graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
+ check, effect, control);
// Specialize the JSCall node to the {target_function}.
NodeProperties::ReplaceValueInput(node, target_function, 0);
@@ -1553,7 +2085,7 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
ConstructParameters const& p = ConstructParametersOf(node->op());
DCHECK_LE(2u, p.arity());
- int const arity = static_cast<int>(p.arity() - 2);
+ int arity = static_cast<int>(p.arity() - 2);
Node* target = NodeProperties::GetValueInput(node, 0);
Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
Node* effect = NodeProperties::GetEffectInput(node);
@@ -1588,7 +2120,8 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
array_function);
effect =
- graph()->NewNode(simplified()->CheckIf(), check, effect, control);
+ graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
+ check, effect, control);
// Turn the {node} into a {JSCreateArray} call.
NodeProperties::ReplaceEffectInput(node, effect);
@@ -1610,7 +2143,8 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
new_target, new_target_feedback);
effect =
- graph()->NewNode(simplified()->CheckIf(), check, effect, control);
+ graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
+ check, effect, control);
// Specialize the JSConstruct node to the {new_target_feedback}.
NodeProperties::ReplaceValueInput(node, new_target_feedback, arity + 1);
@@ -1629,18 +2163,18 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
// Try to specialize JSConstruct {node}s with constant {target}s.
HeapObjectMatcher m(target);
if (m.HasValue()) {
+ // Raise a TypeError if the {target} is not a constructor.
+ if (!m.Value()->IsConstructor()) {
+ NodeProperties::ReplaceValueInputs(node, target);
+ NodeProperties::ChangeOp(node,
+ javascript()->CallRuntime(
+ Runtime::kThrowConstructedNonConstructable));
+ return Changed(node);
+ }
+
if (m.Value()->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
- // Raise a TypeError if the {target} is not a constructor.
- if (!function->IsConstructor()) {
- NodeProperties::ReplaceValueInputs(node, target);
- NodeProperties::ChangeOp(
- node, javascript()->CallRuntime(
- Runtime::kThrowConstructedNonConstructable));
- return Changed(node);
- }
-
// Don't inline cross native context.
if (function->native_context() != *native_context()) return NoChange();
@@ -1678,9 +2212,86 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
return Changed(node);
}
}
+ } else if (m.Value()->IsJSBoundFunction()) {
+ Handle<JSBoundFunction> function =
+ Handle<JSBoundFunction>::cast(m.Value());
+ Handle<JSReceiver> bound_target_function(
+ function->bound_target_function(), isolate());
+ Handle<FixedArray> bound_arguments(function->bound_arguments(),
+ isolate());
+
+ // Patch {node} to use [[BoundTargetFunction]].
+ NodeProperties::ReplaceValueInput(
+ node, jsgraph()->Constant(bound_target_function), 0);
+
+ // Patch {node} to use [[BoundTargetFunction]]
+ // as new.target if {new_target} equals {target}.
+ NodeProperties::ReplaceValueInput(
+ node,
+ graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
+ graph()->NewNode(simplified()->ReferenceEqual(),
+ target, new_target),
+ jsgraph()->Constant(bound_target_function),
+ new_target),
+ arity + 1);
+
+ // Insert the [[BoundArguments]] for {node}.
+ for (int i = 0; i < bound_arguments->length(); ++i) {
+ node->InsertInput(
+ graph()->zone(), i + 1,
+ jsgraph()->Constant(handle(bound_arguments->get(i), isolate())));
+ arity++;
+ }
+
+ // Update the JSConstruct operator on {node}.
+ NodeProperties::ChangeOp(
+ node,
+ javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair()));
+
+ // Try to further reduce the JSConstruct {node}.
+ Reduction const reduction = ReduceJSConstruct(node);
+ return reduction.Changed() ? reduction : Changed(node);
}
- // TODO(bmeurer): Also support optimizing bound functions and proxies here.
+ // TODO(bmeurer): Also support optimizing proxies here.
+ }
+
+ // If {target} is the result of a JSCreateBoundFunction operation,
+ // we can just fold the construction and construct the bound target
+ // function directly instead.
+ if (target->opcode() == IrOpcode::kJSCreateBoundFunction) {
+ Node* bound_target_function = NodeProperties::GetValueInput(target, 0);
+ int const bound_arguments_length =
+ static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity());
+
+ // Patch the {node} to use [[BoundTargetFunction]].
+ NodeProperties::ReplaceValueInput(node, bound_target_function, 0);
+
+ // Patch {node} to use [[BoundTargetFunction]]
+ // as new.target if {new_target} equals {target}.
+ NodeProperties::ReplaceValueInput(
+ node,
+ graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
+ graph()->NewNode(simplified()->ReferenceEqual(),
+ target, new_target),
+ bound_target_function, new_target),
+ arity + 1);
+
+ // Insert the [[BoundArguments]] for {node}.
+ for (int i = 0; i < bound_arguments_length; ++i) {
+ Node* value = NodeProperties::GetValueInput(target, 2 + i);
+ node->InsertInput(graph()->zone(), 1 + i, value);
+ arity++;
+ }
+
+ // Update the JSConstruct operator on {node}.
+ NodeProperties::ChangeOp(
+ node,
+ javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair()));
+
+ // Try to further reduce the JSConstruct {node}.
+ Reduction const reduction = ReduceJSConstruct(node);
+ return reduction.Changed() ? reduction : Changed(node);
}
return NoChange();