aboutsummaryrefslogtreecommitdiff
path: root/src/compiler/js-call-reducer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/js-call-reducer.cc')
-rw-r--r--src/compiler/js-call-reducer.cc522
1 files changed, 402 insertions, 120 deletions
diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc
index e48fce91..c0deb915 100644
--- a/src/compiler/js-call-reducer.cc
+++ b/src/compiler/js-call-reducer.cc
@@ -4,11 +4,15 @@
#include "src/compiler/js-call-reducer.h"
+#include "src/code-factory.h"
+#include "src/code-stubs.h"
+#include "src/compilation-dependencies.h"
#include "src/compiler/js-graph.h"
+#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/simplified-operator.h"
+#include "src/feedback-vector-inl.h"
#include "src/objects-inl.h"
-#include "src/type-feedback-vector-inl.h"
namespace v8 {
namespace internal {
@@ -16,10 +20,14 @@ namespace compiler {
Reduction JSCallReducer::Reduce(Node* node) {
switch (node->opcode()) {
- case IrOpcode::kJSCallConstruct:
- return ReduceJSCallConstruct(node);
- case IrOpcode::kJSCallFunction:
- return ReduceJSCallFunction(node);
+ case IrOpcode::kJSConstruct:
+ return ReduceJSConstruct(node);
+ case IrOpcode::kJSConstructWithSpread:
+ return ReduceJSConstructWithSpread(node);
+ case IrOpcode::kJSCall:
+ return ReduceJSCall(node);
+ case IrOpcode::kJSCallWithSpread:
+ return ReduceJSCallWithSpread(node);
default:
break;
}
@@ -29,9 +37,9 @@ Reduction JSCallReducer::Reduce(Node* node) {
// ES6 section 22.1.1 The Array Constructor
Reduction JSCallReducer::ReduceArrayConstructor(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* target = NodeProperties::GetValueInput(node, 0);
- CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
+ CallParameters const& p = CallParametersOf(node->op());
// Check if we have an allocation site from the CallIC.
Handle<AllocationSite> site;
@@ -58,8 +66,8 @@ Reduction JSCallReducer::ReduceArrayConstructor(Node* node) {
// ES6 section 20.1.1 The Number Constructor
Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
- CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
// Turn the {node} into a {JSToNumber} call.
DCHECK_LE(2u, p.arity());
@@ -73,9 +81,13 @@ Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
// ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* target = NodeProperties::GetValueInput(node, 0);
- CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
+ CallParameters const& p = CallParametersOf(node->op());
+ // Tail calls to Function.prototype.apply are not properly supported
+ // down the pipeline, so we disable this optimization completely for
+ // tail calls (for now).
+ if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange();
Handle<JSFunction> apply =
Handle<JSFunction>::cast(HeapObjectMatcher(target).Value());
size_t arity = p.arity();
@@ -101,35 +113,65 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
if (edge.from() == node) continue;
return NoChange();
}
+ // Check if the arguments can be handled in the fast case (i.e. we don't
+ // have aliased sloppy arguments), and compute the {start_index} for
+ // rest parameters.
+ CreateArgumentsType const type = CreateArgumentsTypeOf(arg_array->op());
+ Node* frame_state = NodeProperties::GetFrameStateInput(arg_array);
+ FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
+ int formal_parameter_count;
+ int start_index = 0;
+ {
+ Handle<SharedFunctionInfo> shared;
+ if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
+ formal_parameter_count = shared->internal_formal_parameter_count();
+ }
+ if (type == CreateArgumentsType::kMappedArguments) {
+ // Mapped arguments (sloppy mode) cannot be handled if they are aliased.
+ if (formal_parameter_count != 0) return NoChange();
+ } else if (type == CreateArgumentsType::kRestParameter) {
+ start_index = formal_parameter_count;
+ }
+ // Check if are applying to inlined arguments or to the arguments of
+ // the outermost function.
+ Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
+ if (outer_state->opcode() != IrOpcode::kFrameState) {
+ // TODO(jarin,bmeurer): Support the NewUnmappedArgumentsElement and
+ // NewRestParameterElements in the EscapeAnalysis and Deoptimizer
+ // instead, then we don't need this hack.
+ // Only works with zero formal parameters because of lacking deoptimizer
+ // support.
+ if (type != CreateArgumentsType::kRestParameter &&
+ formal_parameter_count == 0) {
+ // There are no other uses of the {arg_array} except in StateValues,
+ // so we just replace {arg_array} with a marker for the Deoptimizer
+ // that this refers to the arguments object.
+ Node* arguments = graph()->NewNode(common()->ArgumentsObjectState());
+ ReplaceWithValue(arg_array, arguments);
+ }
+
+ // Reduce {node} to a JSCallForwardVarargs operation, which just
+ // re-pushes the incoming arguments and calls the {target}.
+ node->RemoveInput(0); // Function.prototype.apply
+ node->RemoveInput(2); // arguments
+ NodeProperties::ChangeOp(node, javascript()->CallForwardVarargs(
+ start_index, p.tail_call_mode()));
+ return Changed(node);
+ }
// Get to the actual frame state from which to extract the arguments;
// we can only optimize this in case the {node} was already inlined into
// some other function (and same for the {arg_array}).
- CreateArgumentsType type = CreateArgumentsTypeOf(arg_array->op());
- Node* frame_state = NodeProperties::GetFrameStateInput(arg_array);
- Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
- if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange();
FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
// Need to take the parameters from the arguments adaptor.
frame_state = outer_state;
}
- FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
- int start_index = 0;
- if (type == CreateArgumentsType::kMappedArguments) {
- // Mapped arguments (sloppy mode) cannot be handled if they are aliased.
- Handle<SharedFunctionInfo> shared;
- if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
- if (shared->internal_formal_parameter_count() != 0) return NoChange();
- } else if (type == CreateArgumentsType::kRestParameter) {
- Handle<SharedFunctionInfo> shared;
- if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
- start_index = shared->internal_formal_parameter_count();
- }
// Remove the argArray input from the {node}.
node->RemoveInput(static_cast<int>(--arity));
- // Add the actual parameters to the {node}, skipping the receiver.
+ // Add the actual parameters to the {node}, skipping the receiver,
+ // starting from {start_index}.
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
- for (int i = start_index + 1; i < state_info.parameter_count(); ++i) {
+ for (int i = start_index + 1; i < parameters->InputCount(); ++i) {
node->InsertInput(graph()->zone(), static_cast<int>(arity),
parameters->InputAt(i));
++arity;
@@ -140,24 +182,25 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
} else {
return NoChange();
}
- // Change {node} to the new {JSCallFunction} operator.
+ // Change {node} to the new {JSCall} operator.
NodeProperties::ChangeOp(
- node, javascript()->CallFunction(arity, p.frequency(), VectorSlotPair(),
- convert_mode, p.tail_call_mode()));
+ node,
+ javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode,
+ p.tail_call_mode()));
// Change context of {node} to the Function.prototype.apply context,
// to ensure any exception is thrown in the correct context.
NodeProperties::ReplaceContextInput(
node, jsgraph()->HeapConstant(handle(apply->context(), isolate())));
- // Try to further reduce the JSCallFunction {node}.
- Reduction const reduction = ReduceJSCallFunction(node);
+ // Try to further reduce the JSCall {node}.
+ Reduction const reduction = ReduceJSCall(node);
return reduction.Changed() ? reduction : Changed(node);
}
// ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
- CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
Handle<JSFunction> call = Handle<JSFunction>::cast(
HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value());
// Change context of {node} to the Function.prototype.call context,
@@ -182,83 +225,276 @@ Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
--arity;
}
NodeProperties::ChangeOp(
- node, javascript()->CallFunction(arity, p.frequency(), VectorSlotPair(),
- convert_mode, p.tail_call_mode()));
- // Try to further reduce the JSCallFunction {node}.
- Reduction const reduction = ReduceJSCallFunction(node);
+ node,
+ javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode,
+ p.tail_call_mode()));
+ // Try to further reduce the JSCall {node}.
+ Reduction const reduction = ReduceJSCall(node);
return reduction.Changed() ? reduction : Changed(node);
}
+// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V)
+Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* object = (node->op()->ValueInputCount() >= 3)
+ ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+ Node* context = NodeProperties::GetContextInput(node);
+ Node* frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+
+ // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the
+ // stack trace doesn't contain the @@hasInstance call; we have the
+ // corresponding bug in the baseline case. Some massaging of the frame
+ // state would be necessary here.
+
+ // Morph this {node} into a JSOrdinaryHasInstance node.
+ node->ReplaceInput(0, receiver);
+ node->ReplaceInput(1, object);
+ node->ReplaceInput(2, context);
+ node->ReplaceInput(3, frame_state);
+ node->ReplaceInput(4, effect);
+ node->ReplaceInput(5, control);
+ node->TrimInputCount(6);
+ NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
+ return Changed(node);
+}
+
namespace {
-// TODO(turbofan): Shall we move this to the NodeProperties? Or some (untyped)
-// alias analyzer?
-bool IsSame(Node* a, Node* b) {
- if (a == b) {
- return true;
- } else if (a->opcode() == IrOpcode::kCheckHeapObject) {
- return IsSame(a->InputAt(0), b);
- } else if (b->opcode() == IrOpcode::kCheckHeapObject) {
- return IsSame(a, b->InputAt(0));
+bool CanInlineApiCall(Isolate* isolate, Node* node,
+ Handle<FunctionTemplateInfo> function_template_info) {
+ DCHECK(node->opcode() == IrOpcode::kJSCall);
+ if (V8_UNLIKELY(FLAG_runtime_stats)) return false;
+ if (function_template_info->call_code()->IsUndefined(isolate)) {
+ return false;
+ }
+ CallParameters const& params = CallParametersOf(node->op());
+ // 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.
+ int const argc = static_cast<int>(params.arity()) - 2;
+ if (argc > CallApiCallbackStub::kArgMax || !params.feedback().IsValid()) {
+ return false;
}
- return false;
+ HeapObjectMatcher receiver(NodeProperties::GetValueInput(node, 1));
+ if (!receiver.HasValue()) {
+ return false;
+ }
+ return receiver.Value()->IsUndefined(isolate) ||
+ (receiver.Value()->map()->IsJSObjectMap() &&
+ !receiver.Value()->map()->is_access_check_needed());
}
-// TODO(turbofan): Share with similar functionality in JSInliningHeuristic
-// and JSNativeContextSpecialization, i.e. move to NodeProperties helper?!
-MaybeHandle<Map> InferReceiverMap(Node* node) {
- Node* receiver = NodeProperties::GetValueInput(node, 1);
- Node* effect = NodeProperties::GetEffectInput(node);
- // Check if the {node} is dominated by a CheckMaps with a single map
- // for the {receiver}, and if so use that map for the lowering below.
- for (Node* dominator = effect;;) {
- if (dominator->opcode() == IrOpcode::kCheckMaps &&
- IsSame(dominator->InputAt(0), receiver)) {
- if (dominator->op()->ValueInputCount() == 2) {
- HeapObjectMatcher m(dominator->InputAt(1));
- if (m.HasValue()) return Handle<Map>::cast(m.Value());
- }
- return MaybeHandle<Map>();
- }
- if (dominator->op()->EffectInputCount() != 1) {
- // Didn't find any appropriate CheckMaps node.
- return MaybeHandle<Map>();
+} // namespace
+
+JSCallReducer::HolderLookup JSCallReducer::LookupHolder(
+ Handle<JSObject> object,
+ Handle<FunctionTemplateInfo> function_template_info,
+ Handle<JSObject>* holder) {
+ DCHECK(object->map()->IsJSObjectMap());
+ Handle<Map> object_map(object->map());
+ Handle<FunctionTemplateInfo> expected_receiver_type;
+ if (!function_template_info->signature()->IsUndefined(isolate())) {
+ expected_receiver_type =
+ handle(FunctionTemplateInfo::cast(function_template_info->signature()));
+ }
+ if (expected_receiver_type.is_null() ||
+ expected_receiver_type->IsTemplateFor(*object_map)) {
+ *holder = Handle<JSObject>::null();
+ return kHolderIsReceiver;
+ }
+ while (object_map->has_hidden_prototype()) {
+ Handle<JSObject> prototype(JSObject::cast(object_map->prototype()));
+ object_map = handle(prototype->map());
+ if (expected_receiver_type->IsTemplateFor(*object_map)) {
+ *holder = prototype;
+ return kHolderFound;
}
- dominator = NodeProperties::GetEffectInput(dominator);
}
+ return kHolderNotFound;
}
-} // namespace
-
// ES6 section B.2.2.1.1 get Object.prototype.__proto__
Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* effect = NodeProperties::GetEffectInput(node);
// Try to determine the {receiver} map.
- Handle<Map> receiver_map;
- if (InferReceiverMap(node).ToHandle(&receiver_map)) {
- // Check if we can constant-fold the {receiver} map.
- if (!receiver_map->IsJSProxyMap() &&
- !receiver_map->has_hidden_prototype() &&
- !receiver_map->is_access_check_needed()) {
- Handle<Object> receiver_prototype(receiver_map->prototype(), isolate());
- Node* value = jsgraph()->Constant(receiver_prototype);
- ReplaceWithValue(node, value);
- return Replace(value);
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kReliableReceiverMaps) {
+ Handle<Map> candidate_map(
+ receiver_maps[0]->GetPrototypeChainRootMap(isolate()));
+ Handle<Object> candidate_prototype(candidate_map->prototype(), isolate());
+
+ // Check if we can constant-fold the {candidate_prototype}.
+ for (size_t i = 0; i < receiver_maps.size(); ++i) {
+ Handle<Map> const receiver_map(
+ receiver_maps[i]->GetPrototypeChainRootMap(isolate()));
+ if (receiver_map->IsJSProxyMap() ||
+ receiver_map->has_hidden_prototype() ||
+ receiver_map->is_access_check_needed() ||
+ receiver_map->prototype() != *candidate_prototype) {
+ return NoChange();
+ }
}
+ Node* value = jsgraph()->Constant(candidate_prototype);
+ ReplaceWithValue(node, value);
+ return Replace(value);
}
return NoChange();
}
-Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
- CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
+Reduction JSCallReducer::ReduceCallApiFunction(
+ Node* node, Node* target,
+ Handle<FunctionTemplateInfo> function_template_info) {
+ Isolate* isolate = this->isolate();
+ CHECK(!isolate->serializer_enabled());
+ HeapObjectMatcher m(target);
+ DCHECK(m.HasValue() && m.Value()->IsJSFunction());
+ if (!CanInlineApiCall(isolate, node, function_template_info)) {
+ return NoChange();
+ }
+ Handle<CallHandlerInfo> call_handler_info(
+ handle(CallHandlerInfo::cast(function_template_info->call_code())));
+ Handle<Object> data(call_handler_info->data(), isolate);
+
+ Node* receiver_node = NodeProperties::GetValueInput(node, 1);
+ CallParameters const& params = CallParametersOf(node->op());
+
+ Handle<HeapObject> receiver = HeapObjectMatcher(receiver_node).Value();
+ bool const receiver_is_undefined = receiver->IsUndefined(isolate);
+ if (receiver_is_undefined) {
+ receiver = handle(Handle<JSFunction>::cast(m.Value())->global_proxy());
+ } else {
+ DCHECK(receiver->map()->IsJSObjectMap() &&
+ !receiver->map()->is_access_check_needed());
+ }
+
+ Handle<JSObject> holder;
+ HolderLookup lookup = LookupHolder(Handle<JSObject>::cast(receiver),
+ function_template_info, &holder);
+ if (lookup == kHolderNotFound) return NoChange();
+ if (receiver_is_undefined) {
+ receiver_node = jsgraph()->HeapConstant(receiver);
+ NodeProperties::ReplaceValueInput(node, receiver_node, 1);
+ }
+ Node* holder_node =
+ lookup == kHolderFound ? jsgraph()->HeapConstant(holder) : receiver_node;
+
+ Zone* zone = graph()->zone();
+ // Same as CanInlineApiCall: exclude the target (which goes in a register) and
+ // the receiver (which is implicitly counted by CallApiCallbackStub) from the
+ // arguments count.
+ int const argc = static_cast<int>(params.arity() - 2);
+ CallApiCallbackStub stub(isolate, argc, data->IsUndefined(isolate), false);
+ CallInterfaceDescriptor cid = stub.GetCallInterfaceDescriptor();
+ CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
+ isolate, zone, cid,
+ cid.GetStackParameterCount() + argc + 1 /* implicit receiver */,
+ CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
+ MachineType::AnyTagged(), 1);
+ ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback()));
+ ExternalReference function_reference(
+ &api_function, ExternalReference::DIRECT_API_CALL, isolate);
+
+ // CallApiCallbackStub's register arguments: code, target, call data, holder,
+ // function address.
+ node->InsertInput(zone, 0, jsgraph()->HeapConstant(stub.GetCode()));
+ node->InsertInput(zone, 2, jsgraph()->Constant(data));
+ node->InsertInput(zone, 3, holder_node);
+ node->InsertInput(zone, 4, jsgraph()->ExternalConstant(function_reference));
+ NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
+ return Changed(node);
+}
+
+Reduction JSCallReducer::ReduceSpreadCall(Node* node, int arity) {
+ DCHECK(node->opcode() == IrOpcode::kJSCallWithSpread ||
+ node->opcode() == IrOpcode::kJSConstructWithSpread);
+
+ // Do check to make sure we can actually avoid iteration.
+ if (!isolate()->initial_array_iterator_prototype_map()->is_stable()) {
+ return NoChange();
+ }
+
+ Node* spread = NodeProperties::GetValueInput(node, arity);
+
+ // Check if spread is an arguments object, and {node} is the only value user
+ // of spread (except for value uses in frame states).
+ if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
+ for (Edge edge : spread->use_edges()) {
+ if (edge.from()->opcode() == IrOpcode::kStateValues) continue;
+ if (!NodeProperties::IsValueEdge(edge)) continue;
+ if (edge.from() == node) continue;
+ return NoChange();
+ }
+
+ // Get to the actual frame state from which to extract the arguments;
+ // we can only optimize this in case the {node} was already inlined into
+ // some other function (and same for the {spread}).
+ CreateArgumentsType type = CreateArgumentsTypeOf(spread->op());
+ Node* frame_state = NodeProperties::GetFrameStateInput(spread);
+ Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
+ if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange();
+ FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
+ if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
+ // Need to take the parameters from the arguments adaptor.
+ frame_state = outer_state;
+ }
+ FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
+ int start_index = 0;
+ if (type == CreateArgumentsType::kMappedArguments) {
+ // Mapped arguments (sloppy mode) cannot be handled if they are aliased.
+ Handle<SharedFunctionInfo> shared;
+ if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
+ if (shared->internal_formal_parameter_count() != 0) return NoChange();
+ } else if (type == CreateArgumentsType::kRestParameter) {
+ Handle<SharedFunctionInfo> shared;
+ if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
+ start_index = shared->internal_formal_parameter_count();
+
+ // Only check the array iterator protector when we have a rest object.
+ if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange();
+ // Add a code dependency on the array iterator protector.
+ dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
+ }
+
+ dependencies()->AssumeMapStable(
+ isolate()->initial_array_iterator_prototype_map());
+
+ node->RemoveInput(arity--);
+
+ // Add the actual parameters to the {node}, skipping the receiver.
+ Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
+ for (int i = start_index + 1; i < state_info.parameter_count(); ++i) {
+ node->InsertInput(graph()->zone(), static_cast<int>(++arity),
+ parameters->InputAt(i));
+ }
+
+ if (node->opcode() == IrOpcode::kJSCallWithSpread) {
+ NodeProperties::ChangeOp(
+ node, javascript()->Call(arity + 1, 7, VectorSlotPair()));
+ } else {
+ NodeProperties::ChangeOp(
+ node, javascript()->Construct(arity + 2, 7, VectorSlotPair()));
+ }
+ return Changed(node);
+}
+
+Reduction JSCallReducer::ReduceJSCall(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
Node* target = NodeProperties::GetValueInput(node, 0);
Node* control = NodeProperties::GetControlInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
- // Try to specialize JSCallFunction {node}s with constant {target}s.
+ // Try to specialize JSCall {node}s with constant {target}s.
HeapObjectMatcher m(target);
if (m.HasValue()) {
if (m.Value()->IsJSFunction()) {
@@ -274,12 +510,17 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
return Changed(node);
}
+ // Don't inline cross native context.
+ if (function->native_context() != *native_context()) return NoChange();
+
// Check for known builtin functions.
switch (shared->code()->builtin_index()) {
case Builtins::kFunctionPrototypeApply:
return ReduceFunctionPrototypeApply(node);
case Builtins::kFunctionPrototypeCall:
return ReduceFunctionPrototypeCall(node);
+ case Builtins::kFunctionPrototypeHasInstance:
+ return ReduceFunctionPrototypeHasInstance(node);
case Builtins::kNumberConstructor:
return ReduceNumberConstructor(node);
case Builtins::kObjectPrototypeGetProto:
@@ -292,6 +533,12 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
if (*function == function->native_context()->array_function()) {
return ReduceArrayConstructor(node);
}
+
+ if (shared->IsApiFunction()) {
+ return ReduceCallApiFunction(
+ node, target,
+ handle(FunctionTemplateInfo::cast(shared->function_data())));
+ }
} else if (m.Value()->IsJSBoundFunction()) {
Handle<JSBoundFunction> function =
Handle<JSBoundFunction>::cast(m.Value());
@@ -300,9 +547,9 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
Handle<Object> bound_this(function->bound_this(), isolate());
Handle<FixedArray> bound_arguments(function->bound_arguments(),
isolate());
- CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
+ CallParameters const& p = CallParametersOf(node->op());
ConvertReceiverMode const convert_mode =
- (bound_this->IsNull(isolate()) || bound_this->IsUndefined(isolate()))
+ (bound_this->IsNullOrUndefined(isolate()))
? ConvertReceiverMode::kNullOrUndefined
: ConvertReceiverMode::kNotNullOrUndefined;
size_t arity = p.arity();
@@ -319,11 +566,12 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
jsgraph()->Constant(handle(bound_arguments->get(i), isolate())));
arity++;
}
- NodeProperties::ChangeOp(node, javascript()->CallFunction(
- arity, p.frequency(), VectorSlotPair(),
- convert_mode, p.tail_call_mode()));
- // Try to further reduce the JSCallFunction {node}.
- Reduction const reduction = ReduceJSCallFunction(node);
+ NodeProperties::ChangeOp(
+ node,
+ javascript()->Call(arity, p.frequency(), VectorSlotPair(),
+ convert_mode, p.tail_call_mode()));
+ // Try to further reduce the JSCall {node}.
+ Reduction const reduction = ReduceJSCall(node);
return reduction.Changed() ? reduction : Changed(node);
}
@@ -332,26 +580,36 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
return NoChange();
}
- // Not much we can do if deoptimization support is disabled.
- if (!(flags() & kDeoptimizationEnabled)) return NoChange();
-
// Extract feedback from the {node} using the CallICNexus.
if (!p.feedback().IsValid()) return NoChange();
CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
- if (nexus.IsUninitialized() && (flags() & kBailoutOnUninitialized)) {
- Node* frame_state = NodeProperties::FindFrameStateBefore(node);
- Node* deoptimize = graph()->NewNode(
- common()->Deoptimize(
- DeoptimizeKind::kSoft,
- DeoptimizeReason::kInsufficientTypeFeedbackForCall),
- frame_state, effect, control);
- // TODO(bmeurer): This should be on the AdvancedReducer somehow.
- NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
- Revisit(graph()->end());
- node->TrimInputCount(0);
- NodeProperties::ChangeOp(node, common()->Dead());
+ if (nexus.IsUninitialized()) {
+ // TODO(turbofan): Tail-calling to a CallIC stub is not supported.
+ if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange();
+
+ // Insert a CallIC here to collect feedback for uninitialized calls.
+ int const arg_count = static_cast<int>(p.arity() - 2);
+ Callable callable = CodeFactory::CallIC(isolate(), p.convert_mode());
+ CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
+ CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
+ isolate(), graph()->zone(), callable.descriptor(), arg_count + 1,
+ flags);
+ Node* stub_code = jsgraph()->HeapConstant(callable.code());
+ Node* stub_arity = jsgraph()->Constant(arg_count);
+ Node* slot_index =
+ jsgraph()->Constant(FeedbackVector::GetIndex(p.feedback().slot()));
+ Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector());
+ node->InsertInput(graph()->zone(), 0, stub_code);
+ node->InsertInput(graph()->zone(), 2, stub_arity);
+ node->InsertInput(graph()->zone(), 3, slot_index);
+ node->InsertInput(graph()->zone(), 4, feedback_vector);
+ NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
}
+
+ // Not much we can do if deoptimization support is disabled.
+ if (!(flags() & kDeoptimizationEnabled)) return NoChange();
+
Handle<Object> feedback(nexus.GetFeedback(), isolate());
if (feedback->IsAllocationSite()) {
// Retrieve the Array function from the {node}.
@@ -379,22 +637,30 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
effect =
graph()->NewNode(simplified()->CheckIf(), check, effect, control);
- // Specialize the JSCallFunction node to the {target_function}.
+ // Specialize the JSCall node to the {target_function}.
NodeProperties::ReplaceValueInput(node, target_function, 0);
NodeProperties::ReplaceEffectInput(node, effect);
- // Try to further reduce the JSCallFunction {node}.
- Reduction const reduction = ReduceJSCallFunction(node);
+ // Try to further reduce the JSCall {node}.
+ Reduction const reduction = ReduceJSCall(node);
return reduction.Changed() ? reduction : Changed(node);
}
}
return NoChange();
}
+Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode());
+ CallWithSpreadParameters const& p = CallWithSpreadParametersOf(node->op());
+ DCHECK_LE(3u, p.arity());
+ int arity = static_cast<int>(p.arity() - 1);
+
+ return ReduceSpreadCall(node, arity);
+}
-Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode());
- CallConstructParameters const& p = CallConstructParametersOf(node->op());
+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);
Node* target = NodeProperties::GetValueInput(node, 0);
@@ -402,7 +668,7 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
- // Try to specialize JSCallConstruct {node}s with constant {target}s.
+ // Try to specialize JSConstruct {node}s with constant {target}s.
HeapObjectMatcher m(target);
if (m.HasValue()) {
if (m.Value()->IsJSFunction()) {
@@ -412,10 +678,14 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
if (!function->IsConstructor()) {
NodeProperties::ReplaceValueInputs(node, target);
NodeProperties::ChangeOp(
- node, javascript()->CallRuntime(Runtime::kThrowCalledNonCallable));
+ node, javascript()->CallRuntime(
+ Runtime::kThrowConstructedNonConstructable));
return Changed(node);
}
+ // Don't inline cross native context.
+ if (function->native_context() != *native_context()) return NoChange();
+
// Check for the ArrayConstructor.
if (*function == function->native_context()->array_function()) {
// Check if we have an allocation site.
@@ -487,15 +757,15 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
effect =
graph()->NewNode(simplified()->CheckIf(), check, effect, control);
- // Specialize the JSCallConstruct node to the {target_function}.
+ // Specialize the JSConstruct node to the {target_function}.
NodeProperties::ReplaceValueInput(node, target_function, 0);
NodeProperties::ReplaceEffectInput(node, effect);
if (target == new_target) {
NodeProperties::ReplaceValueInput(node, target_function, arity + 1);
}
- // Try to further reduce the JSCallConstruct {node}.
- Reduction const reduction = ReduceJSCallConstruct(node);
+ // Try to further reduce the JSConstruct {node}.
+ Reduction const reduction = ReduceJSConstruct(node);
return reduction.Changed() ? reduction : Changed(node);
}
}
@@ -503,10 +773,22 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
return NoChange();
}
+Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode());
+ ConstructWithSpreadParameters const& p =
+ ConstructWithSpreadParametersOf(node->op());
+ DCHECK_LE(3u, p.arity());
+ int arity = static_cast<int>(p.arity() - 2);
+
+ return ReduceSpreadCall(node, arity);
+}
+
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
+Factory* JSCallReducer::factory() const { return isolate()->factory(); }
+
CommonOperatorBuilder* JSCallReducer::common() const {
return jsgraph()->common();
}